Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit bb86bee3994834129cbb3c668c94920bf9daeeb0


Parents : 16e055b
Author : Mark Qvist <mark@unsigned.io>
Date : 2024-06-02T18:31:58+02:00

Updated, modded and refactored plyer

Changes

104 files changed, 792 insertions(+), 2474 deletions(-)


Diff

diff --git a/sbapp/plyer/__init__.py b/sbapp/plyer/__init__.py
index ab87901c..9bfb1909 100644
--- a/sbapp/plyer/__init__.py
+++ b/sbapp/plyer/__init__.py
@@ -13,11 +13,15 @@ __all__ = (
'stt', 'temperature', 'tts', 'uniqueid', 'vibrator', 'wifi', 'devicename'
)
-__version__ = '2.1.0.dev0'
-
-
-from plyer import facades
-from plyer.utils import Proxy
+__version__ = '2.2.0.dev0'
+
+import RNS
+if RNS.vendor.platformutils.is_android():
+ from plyer import facades
+ from plyer.utils import Proxy
+else:
+ from sbapp.plyer import facades
+ from sbapp.plyer.utils import Proxy
#: Accelerometer proxy to :class:`plyer.facades.Accelerometer`
accelerometer = Proxy('accelerometer', facades.Accelerometer)

diff --git a/sbapp/plyer/facades/__init__.py b/sbapp/plyer/facades/__init__.py
index c1e25605..81a0f631 100644
--- a/sbapp/plyer/facades/__init__.py
+++ b/sbapp/plyer/facades/__init__.py
@@ -14,38 +14,76 @@ __all__ = ('Accelerometer', 'Audio', 'Barometer', 'Battery', 'Call', 'Camera',
'Processors', 'StoragePath', 'Keystore', 'Bluetooth', 'Screenshot',
'STT', 'DeviceName')
-from plyer.facades.accelerometer import Accelerometer
-from plyer.facades.audio import Audio
-from plyer.facades.barometer import Barometer
-from plyer.facades.battery import Battery
-from plyer.facades.call import Call
-from plyer.facades.camera import Camera
-from plyer.facades.compass import Compass
-from plyer.facades.email import Email
-from plyer.facades.filechooser import FileChooser
-from plyer.facades.flash import Flash
-from plyer.facades.gps import GPS
-from plyer.facades.gravity import Gravity
-from plyer.facades.gyroscope import Gyroscope
-from plyer.facades.irblaster import IrBlaster
-from plyer.facades.light import Light
-from plyer.facades.proximity import Proximity
-from plyer.facades.orientation import Orientation
-from plyer.facades.notification import Notification
-from plyer.facades.sms import Sms
-from plyer.facades.stt import STT
-from plyer.facades.tts import TTS
-from plyer.facades.uniqueid import UniqueID
-from plyer.facades.vibrator import Vibrator
-from plyer.facades.wifi import Wifi
-from plyer.facades.temperature import Temperature
-from plyer.facades.humidity import Humidity
-from plyer.facades.spatialorientation import SpatialOrientation
-from plyer.facades.brightness import Brightness
-from plyer.facades.keystore import Keystore
-from plyer.facades.storagepath import StoragePath
-from plyer.facades.bluetooth import Bluetooth
-from plyer.facades.processors import Processors
-from plyer.facades.cpu import CPU
-from plyer.facades.screenshot import Screenshot
-from plyer.facades.devicename import DeviceName
+import RNS
+if RNS.vendor.platformutils.is_android():
+ from plyer.facades.accelerometer import Accelerometer
+ from plyer.facades.audio import Audio
+ from plyer.facades.barometer import Barometer
+ from plyer.facades.battery import Battery
+ from plyer.facades.call import Call
+ from plyer.facades.camera import Camera
+ from plyer.facades.compass import Compass
+ from plyer.facades.email import Email
+ from plyer.facades.filechooser import FileChooser
+ from plyer.facades.flash import Flash
+ from plyer.facades.gps import GPS
+ from plyer.facades.gravity import Gravity
+ from plyer.facades.gyroscope import Gyroscope
+ from plyer.facades.irblaster import IrBlaster
+ from plyer.facades.light import Light
+ from plyer.facades.proximity import Proximity
+ from plyer.facades.orientation import Orientation
+ from plyer.facades.notification import Notification
+ from plyer.facades.sms import Sms
+ from plyer.facades.stt import STT
+ from plyer.facades.tts import TTS
+ from plyer.facades.uniqueid import UniqueID
+ from plyer.facades.vibrator import Vibrator
+ from plyer.facades.wifi import Wifi
+ from plyer.facades.temperature import Temperature
+ from plyer.facades.humidity import Humidity
+ from plyer.facades.spatialorientation import SpatialOrientation
+ from plyer.facades.brightness import Brightness
+ from plyer.facades.keystore import Keystore
+ from plyer.facades.storagepath import StoragePath
+ from plyer.facades.bluetooth import Bluetooth
+ from plyer.facades.processors import Processors
+ from plyer.facades.cpu import CPU
+ from plyer.facades.screenshot import Screenshot
+ from plyer.facades.devicename import DeviceName
+else:
+ from sbapp.plyer.facades.accelerometer import Accelerometer
+ from sbapp.plyer.facades.audio import Audio
+ from sbapp.plyer.facades.barometer import Barometer
+ from sbapp.plyer.facades.battery import Battery
+ from sbapp.plyer.facades.call import Call
+ from sbapp.plyer.facades.camera import Camera
+ from sbapp.plyer.facades.compass import Compass
+ from sbapp.plyer.facades.email import Email
+ from sbapp.plyer.facades.filechooser import FileChooser
+ from sbapp.plyer.facades.flash import Flash
+ from sbapp.plyer.facades.gps import GPS
+ from sbapp.plyer.facades.gravity import Gravity
+ from sbapp.plyer.facades.gyroscope import Gyroscope
+ from sbapp.plyer.facades.irblaster import IrBlaster
+ from sbapp.plyer.facades.light import Light
+ from sbapp.plyer.facades.proximity import Proximity
+ from sbapp.plyer.facades.orientation import Orientation
+ from sbapp.plyer.facades.notification import Notification
+ from sbapp.plyer.facades.sms import Sms
+ from sbapp.plyer.facades.stt import STT
+ from sbapp.plyer.facades.tts import TTS
+ from sbapp.plyer.facades.uniqueid import UniqueID
+ from sbapp.plyer.facades.vibrator import Vibrator
+ from sbapp.plyer.facades.wifi import Wifi
+ from sbapp.plyer.facades.temperature import Temperature
+ from sbapp.plyer.facades.humidity import Humidity
+ from sbapp.plyer.facades.spatialorientation import SpatialOrientation
+ from sbapp.plyer.facades.brightness import Brightness
+ from sbapp.plyer.facades.keystore import Keystore
+ from sbapp.plyer.facades.storagepath import StoragePath
+ from sbapp.plyer.facades.bluetooth import Bluetooth
+ from sbapp.plyer.facades.processors import Processors
+ from sbapp.plyer.facades.cpu import CPU
+ from sbapp.plyer.facades.screenshot import Screenshot
+ from sbapp.plyer.facades.devicename import DeviceName

diff --git a/sbapp/plyer/facades/audio.py b/sbapp/plyer/facades/audio.py
index c5e1db5f..03940379 100644
--- a/sbapp/plyer/facades/audio.py
+++ b/sbapp/plyer/facades/audio.py
@@ -94,6 +94,7 @@ class Audio:
# private
def _start(self):
+ raise IOError("JUICE")
raise NotImplementedError()
def _stop(self):

diff --git a/sbapp/plyer/facades/humidity.py b/sbapp/plyer/facades/humidity.py
index ac633330..8d722390 100644
--- a/sbapp/plyer/facades/humidity.py
+++ b/sbapp/plyer/facades/humidity.py
@@ -4,7 +4,7 @@ class Humidity:
With method `enable` you can turn on Humidity sensor and
'disable' method stops the sensor.
Use property `tell` to get humidity value.
-
+
Supported Platforms
-------------------
Android

diff --git a/sbapp/plyer/facades/maps.py b/sbapp/plyer/facades/maps.py
new file mode 100644
index 00000000..ba8d5fea
--- /dev/null
+++ b/sbapp/plyer/facades/maps.py
@@ -0,0 +1,88 @@
+'''
+Maps
+=======
+The :class:`Maps` creates a client for accessing the default Maps API.
+
+Holds features such as opening a location by
+address & latitude/longitude, create queries, or find directions between
+two points
+
+Simple Examples
+---------------
+
+Perform a search::
+
+ >>> from plyer import maps
+ >>> maps.search('Mexican Restaurant')
+ >>> maps.search('Taco Bell', latitude=38.5810606, longitude=-121.493895)
+
+Get directions to a location::
+
+ >>> from plyer import maps
+ >>> maps.route('Cupertino', 'San Francisco')
+ >>> maps.route('41.9156316,-72.6130726', '42.65228271484,-73.7577362060')
+
+View a specific location::
+
+ >>> from plyer import maps
+ >>> maps.open_by_address('25 Leshin Lane, Hightstown, NJ')
+ >>> maps.open_by_lat_long(30.451468, -91.187149)
+ >>> maps.open_by_lat_long(30.451468, -91.187149, name='Home')
+
+Supported Platforms
+-------------------
+macOS, iOS
+---------------
+'''
+
+
+class Maps:
+ '''
+ Maps facade.
+ '''
+
+ def open_by_address(self, address, **kwargs):
+ '''
+ Open the specificed location by address in the default Maps API
+ '''
+ self._open_by_address(address, **kwargs)
+
+ def open_by_lat_long(self, latitude, longitude, **kwargs):
+ '''
+ Open the specificed location by latitude & longitude coordinates
+ in the default Maps API
+ '''
+ self._open_by_lat_long(latitude, longitude, **kwargs)
+
+ def search(self, query, **kwargs):
+ '''
+ The query. This parameter is treated as if its value had been typed
+ into the Maps search field by the user.
+
+ Note that query=* is not supported
+ '''
+ self._search(query, **kwargs)
+
+ def route(self, saddr, daddr, **kwargs):
+ '''
+ To provide navigation directions from one location to another.
+
+ :param saddr: The source address to be used as the starting
+ point for directions.
+
+ :param daddr: The destination address to be used as the
+ destination point for directions.
+ '''
+ self._route(saddr, daddr, **kwargs)
+
+ def _open_by_address(self, address, **kwargs):
+ raise NotImplementedError()
+
+ def _open_by_lat_long(self, latitude, longitude, **kwargs):
+ raise NotImplementedError()
+
+ def _search(self, query, **kwargs):
+ raise NotImplementedError()
+
+ def _route(self, saddr, daddr, **kwargs):
+ raise NotImplementedError()

diff --git a/sbapp/plyer/facades/notification.py b/sbapp/plyer/facades/notification.py
index 71c7d532..db0d2279 100644
--- a/sbapp/plyer/facades/notification.py
+++ b/sbapp/plyer/facades/notification.py
@@ -45,8 +45,8 @@ class Notification:
Notification facade.
'''
- def notify(self, title='', message='', app_name='', app_icon='', notification_icon=None,
- timeout=10, ticker='', toast=False, hints={}, context_override=None):
+ def notify(self, title='', message='', app_name='', app_icon='',
+ timeout=10, ticker='', toast=False, hints={}):
'''
Send a notification.
@@ -83,8 +83,8 @@ class Notification:
self._notify(
title=title, message=message,
- app_icon=app_icon, app_name=app_name, notification_icon=notification_icon,
- timeout=timeout, ticker=ticker, toast=toast, hints=hints, context_override=context_override
+ app_icon=app_icon, app_name=app_name,
+ timeout=timeout, ticker=ticker, toast=toast, hints=hints
)
# private

diff --git a/sbapp/plyer/facades/orientation.py b/sbapp/plyer/facades/orientation.py
index 56ea0bad..077de553 100644
--- a/sbapp/plyer/facades/orientation.py
+++ b/sbapp/plyer/facades/orientation.py
@@ -30,7 +30,7 @@ To set sensor::
Supported Platforms
-------------------
-Android
+Android, Linux
'''

diff --git a/sbapp/plyer/facades/sms.py b/sbapp/plyer/facades/sms.py
index 0e58f44a..59408221 100644
--- a/sbapp/plyer/facades/sms.py
+++ b/sbapp/plyer/facades/sms.py
@@ -23,7 +23,7 @@ To send sms::
Supported Platforms
-------------------
-Android, iOS
+Android, iOS, macOS
'''
@@ -33,17 +33,23 @@ class Sms:
Sms facade.
'''
- def send(self, recipient, message):
+ def send(self, recipient, message, mode=None, **kwargs):
'''
Send SMS or open SMS interface.
+ Includes optional `mode` parameter for macOS that can be set to
+ `'SMS'` if carrier-activated device is correctly paired and
+ configured to macOS.
:param recipient: The receiver
:param message: the message
+ :param mode: (optional, macOS only), can be set to 'iMessage'
+ (default) or 'SMS'
:type recipient: number
:type message: str
+ :type mode: str
'''
- self._send(recipient=recipient, message=message)
+ self._send(recipient=recipient, message=message, mode=mode, **kwargs)
# private

diff --git a/sbapp/plyer/platforms/android/accelerometer.py b/sbapp/plyer/platforms/android/accelerometer.py
index 00c73cfa..7754579f 100644
--- a/sbapp/plyer/platforms/android/accelerometer.py
+++ b/sbapp/plyer/platforms/android/accelerometer.py
@@ -70,7 +70,7 @@ class AndroidAccelerometer(Accelerometer):
return (None, None, None)
def __del__(self):
- if(self.bState):
+ if self.bState:
self._disable()
super().__del__()

diff --git a/sbapp/plyer/platforms/android/audio.py b/sbapp/plyer/platforms/android/audio.py
index 9f000ff4..0b229c45 100644
--- a/sbapp/plyer/platforms/android/audio.py
+++ b/sbapp/plyer/platforms/android/audio.py
@@ -1,3 +1,5 @@
+import time
+import threading
from jnius import autoclass
from plyer.facades.audio import Audio
@@ -20,17 +22,31 @@ class AndroidAudio(Audio):
'''
def __init__(self, file_path=None):
- default_path = '/sdcard/testrecorder.3gp'
+ default_path = None
super().__init__(file_path or default_path)
self._recorder = None
self._player = None
+ self._check_thread = None
+ self._finished_callback = None
+
+ def _check_playback(self):
+ while self._player and self._player.isPlaying():
+ time.sleep(0.25)
+
+ if self._finished_callback and callable(self._finished_callback):
+ self._check_thread = None
+ self._finished_callback(self)
+
def _start(self):
self._recorder = MediaRecorder()
self._recorder.setAudioSource(AudioSource.DEFAULT)
- self._recorder.setOutputFormat(OutputFormat.DEFAULT)
- self._recorder.setAudioEncoder(AudioEncoder.DEFAULT)
+ self._recorder.setAudioSamplingRate(44100)
+ self._recorder.setAudioEncodingBitRate(128000)
+ self._recorder.setAudioChannels(1)
+ self._recorder.setOutputFormat(OutputFormat.MPEG_4)
+ self._recorder.setAudioEncoder(AudioEncoder.AAC)
self._recorder.setOutputFile(self.file_path)
self._recorder.prepare()
@@ -53,6 +69,10 @@ class AndroidAudio(Audio):
self._player.prepare()
self._player.start()
+ self._check_thread = threading.Thread(target=self._check_playback, daemon=True)
+ self._check_thread.start()
+
+
def instance():
return AndroidAudio()

diff --git a/sbapp/plyer/platforms/android/camera.py b/sbapp/plyer/platforms/android/camera.py
index 19707bdc..3384a66d 100644
--- a/sbapp/plyer/platforms/android/camera.py
+++ b/sbapp/plyer/platforms/android/camera.py
@@ -14,7 +14,7 @@ Uri = autoclass('android.net.Uri')
class AndroidCamera(Camera):
def _take_picture(self, on_complete, filename=None):
- assert(on_complete is not None)
+ assert on_complete is not None
self.on_complete = on_complete
self.filename = filename
android.activity.unbind(on_activity_result=self._on_activity_result)
@@ -26,7 +26,7 @@ class AndroidCamera(Camera):
activity.startActivityForResult(intent, 0x123)
def _take_video(self, on_complete, filename=None):
- assert(on_complete is not None)
+ assert on_complete is not None
self.on_complete = on_complete
self.filename = filename
android.activity.unbind(on_activity_result=self._on_activity_result)

diff --git a/sbapp/plyer/platforms/android/compass.py b/sbapp/plyer/platforms/android/compass.py
index fcf0483c..5bb16c62 100644
--- a/sbapp/plyer/platforms/android/compass.py
+++ b/sbapp/plyer/platforms/android/compass.py
@@ -110,7 +110,7 @@ class AndroidCompass(Compass):
return (None, None, None, None, None, None)
def __del__(self):
- if(self.bState):
+ if self.bState:
self._disable()
super().__del__()

diff --git a/sbapp/plyer/platforms/android/filechooser.py b/sbapp/plyer/platforms/android/filechooser.py
index b809ccbb..b8c943cf 100644
--- a/sbapp/plyer/platforms/android/filechooser.py
+++ b/sbapp/plyer/platforms/android/filechooser.py
@@ -43,7 +43,7 @@ using that result will use an incorrect one i.e. the default value of
.. versionadded:: 1.4.0
'''
-from os.path import join, basename
+from os.path import join
from random import randint
from android import activity, mActivity
@@ -62,6 +62,8 @@ Long = autoclass('java.lang.Long')
IMedia = autoclass('android.provider.MediaStore$Images$Media')
VMedia = autoclass('android.provider.MediaStore$Video$Media')
AMedia = autoclass('android.provider.MediaStore$Audio$Media')
+Files = autoclass('android.provider.MediaStore$Files')
+FileOutputStream = autoclass('java.io.FileOutputStream')
class AndroidFileChooser(FileChooser):
@@ -74,6 +76,7 @@ class AndroidFileChooser(FileChooser):
# filechooser activity <-> result pair identification
select_code = None
+ save_code = None
# default selection value
selection = None
@@ -105,6 +108,7 @@ class AndroidFileChooser(FileChooser):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.select_code = randint(123456, 654321)
+ self.save_code = randint(123456, 654321)
self.selection = None
# bind a function for a response from filechooser activity
@@ -139,9 +143,11 @@ class AndroidFileChooser(FileChooser):
# create Intent for opening
file_intent = Intent(Intent.ACTION_GET_CONTENT)
- if not self.selected_mime_type or \
- type(self.selected_mime_type) != str or \
- self.selected_mime_type not in self.mime_type:
+ if (
+ not self.selected_mime_type
+ or not isinstance(self.selected_mime_type, str)
+ or self.selected_mime_type not in self.mime_type
+ ):
file_intent.setType("*/*")
else:
file_intent.setType(self.mime_type[self.selected_mime_type])
@@ -163,6 +169,38 @@ class AndroidFileChooser(FileChooser):
self.select_code
)
+ def _save_file(self, **kwargs):
+ self._save_callback = kwargs.pop("callback")
+
+ title = kwargs.pop("title", None)
+
+ self.selected_mime_type = \
+ kwargs.pop("filters")[0] if "filters" in kwargs else ""
+
+ file_intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
+ if (
+ not self.selected_mime_type
+ or not isinstance(self.selected_mime_type, str)
+ or self.selected_mime_type not in self.mime_type
+ ):
+ file_intent.setType("*/*")
+ else:
+ file_intent.setType(self.mime_type[self.selected_mime_type])
+ file_intent.addCategory(
+ Intent.CATEGORY_OPENABLE
+ )
+
+ if title:
+ file_intent.putExtra(Intent.EXTRA_TITLE, title)
+
+ mActivity.startActivityForResult(
+ Intent.createChooser(file_intent, cast(
+ 'java.lang.CharSequence',
+ String("FileChooser")
+ )),
+ self.save_code
+ )
+
def _on_activity_result(self, request_code, result_code, data):
'''
Listener for ``android.app.Activity.onActivityResult()`` assigned
@@ -171,28 +209,41 @@ class AndroidFileChooser(FileChooser):
.. versionadded:: 1.4.0
'''
- # not our response
- if request_code != self.select_code:
+ # bad data
+ if data is None:
return
if result_code != Activity.RESULT_OK:
# The action had been cancelled.
return
- selection = []
- # Process multiple URI if multiple files selected
- try:
- for count in range(data.getClipData().getItemCount()):
- ele = self._resolve_uri(
- data.getClipData().getItemAt(count).getUri()) or []
- selection.append(ele)
- except Exception:
- selection = [self._resolve_uri(data.getData()), ]
-
- # return value to object
- self.selection = selection
- # return value via callback
- self._handle_selection(selection)
+ if request_code == self.select_code:
+ selection = []
+ # Process multiple URI if multiple files selected
+ try:
+ for count in range(data.getClipData().getItemCount()):
+ ele = self._resolve_uri(
+ data.getClipData().getItemAt(count).getUri()) or []
+ selection.append(ele)
+ except Exception:
+ selection = [self._resolve_uri(data.getData()), ]
+
+ # return value to object
+ self.selection = selection
+ # return value via callback
+ self._handle_selection(selection)
+
+ elif request_code == self.save_code:
+ uri = data.getData()
+
+ with mActivity.getContentResolver().openFileDescriptor(
+ uri, "w"
+ ) as pfd:
+ with FileOutputStream(
+ pfd.getFileDescriptor()
+ ) as fileOutputStream:
+ # return value via callback
+ self._save_callback(fileOutputStream)
@staticmethod
def _handle_external_documents(uri):
@@ -206,28 +257,19 @@ class AndroidFileChooser(FileChooser):
file_id = DocumentsContract.getDocumentId(uri)
file_type, file_name = file_id.split(':')
- # internal SD card mostly mounted as a files storage in phone
- internal = storagepath.get_external_storage_dir()
-
- # external (removable) SD card i.e. microSD
- external = storagepath.get_sdcard_dir()
- try:
- external_base = basename(external)
- except TypeError:
- external_base = basename(internal)
+ primary_storage = storagepath.get_external_storage_dir()
+ sdcard_storage = storagepath.get_sdcard_dir()
- # resolve sdcard path
- sd_card = internal
+ directory = primary_storage
- # because external might have /storage/.../1 or other suffix
- # and file_type might be only a part of the real folder in /storage
- if file_type in external_base or external_base in file_type:
- sd_card = external
+ if file_type == "primary":
+ directory = primary_storage
elif file_type == "home":
- sd_card = join(Environment.getExternalStorageDirectory(
- ).getAbsolutePath(), Environment.DIRECTORY_DOCUMENTS)
+ directory = join(primary_storage, Environment.DIRECTORY_DOCUMENTS)
+ elif sdcard_storage and file_type in sdcard_storage:
+ directory = sdcard_storage
- return join(sd_card, file_name)
+ return join(directory, file_name)
@staticmethod
def _handle_media_documents(uri):
@@ -248,6 +290,11 @@ class AndroidFileChooser(FileChooser):
uri = VMedia.EXTERNAL_CONTENT_URI
elif file_type == 'audio':
uri = AMedia.EXTERNAL_CONTENT_URI
+
+ # Other file type was selected (probably in the Documents folder)
+ else:
+ uri = Files.getContentUri("external")
+
return file_name, selection, uri
@staticmethod
@@ -279,6 +326,23 @@ class AndroidFileChooser(FileChooser):
.. versionadded:: 1.4.0
'''
+ try:
+ download_dir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS
+ ).getPath()
+ path = AndroidFileChooser._parse_content(
+ uri=uri,
+ projection=["_display_name"],
+ selection=None,
+ selection_args=None,
+ sort_order=None,
+ )
+ return join(download_dir, path)
+
+ except Exception:
+ import traceback
+ traceback.print_exc()
+
# known locations, differ between machines
downloads = [
'content://downloads/public_downloads',
@@ -441,6 +505,8 @@ class AndroidFileChooser(FileChooser):
mode = kwargs.pop('mode', None)
if mode == 'open':
self._open_file(**kwargs)
+ elif mode == 'save':
+ self._save_file(**kwargs)
def instance():

diff --git a/sbapp/plyer/platforms/android/gps.py b/sbapp/plyer/platforms/android/gps.py
index b173876f..740b636c 100644
--- a/sbapp/plyer/platforms/android/gps.py
+++ b/sbapp/plyer/platforms/android/gps.py
@@ -86,4 +86,4 @@ class AndroidGPS(GPS):
def instance():
- return AndroidGPS()
\ No newline at end of file
+ return AndroidGPS()

diff --git a/sbapp/plyer/platforms/android/gyroscope.py b/sbapp/plyer/platforms/android/gyroscope.py
index d6382b15..99b224eb 100644
--- a/sbapp/plyer/platforms/android/gyroscope.py
+++ b/sbapp/plyer/platforms/android/gyroscope.py
@@ -110,7 +110,7 @@ class AndroidGyroscope(Gyroscope):
return (None, None, None, None, None, None)
def __del__(self):
- if(self.bState):
+ if self.bState:
self._disable()
super().__del__()

diff --git a/sbapp/plyer/platforms/android/notification.py b/sbapp/plyer/platforms/android/notification.py
index ac9f76b7..ee0de2d6 100644
--- a/sbapp/plyer/platforms/android/notification.py
+++ b/sbapp/plyer/platforms/android/notification.py
@@ -154,10 +154,15 @@ class AndroidNotification(Notification):
notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
notification_intent.setAction(Intent.ACTION_MAIN)
notification_intent.addCategory(Intent.CATEGORY_LAUNCHER)
+ if SDK_INT >= 23:
+ # FLAG_IMMUTABLE added in SDK 23, required since SDK 31:
+ pending_flags = PendingIntent.FLAG_IMMUTABLE
+ else:
+ pending_flags = 0
# get our application Activity
pending_intent = PendingIntent.getActivity(
- app_context, 0, notification_intent, 0
+ app_context, 0, notification_intent, pending_flags
)
notification.setContentIntent(pending_intent)
@@ -179,8 +184,6 @@ class AndroidNotification(Notification):
kwargs.get('title', '').encode('utf-8')
)
icon = kwargs.get('app_icon')
- notification_icon = kwargs.get('notification_icon')
- context_override = kwargs.get('context_override')
# decide whether toast only or proper notification
if kwargs.get('toast'):

diff --git a/sbapp/plyer/platforms/android/storagepath.py b/sbapp/plyer/platforms/android/storagepath.py
index 788e3fce..d18ea614 100755
--- a/sbapp/plyer/platforms/android/storagepath.py
+++ b/sbapp/plyer/platforms/android/storagepath.py
@@ -3,14 +3,13 @@ Android Storage Path
--------------------
'''
-from os import listdir, access, R_OK
-from os.path import join
from plyer.facades import StoragePath
-from jnius import autoclass
+from plyer.platforms.android import SDK_INT
+from jnius import autoclass, cast
from android import mActivity
-Environment = autoclass('android.os.Environment')
-Context = autoclass('android.content.Context')
+Environment = autoclass("android.os.Environment")
+Context = autoclass("android.content.Context")
class AndroidStoragePath(StoragePath):
@@ -25,17 +24,29 @@ class AndroidStoragePath(StoragePath):
'''
.. versionadded:: 1.4.0
'''
- # folder in /storage/ that is readable
- # and is not internal SD card
path = None
- for folder in listdir('/storage'):
- folder = join('/storage', folder)
- if folder in self._get_external_storage_dir():
- continue
- if not access(folder, R_OK):
- continue
- path = folder
- break
+ context = mActivity.getApplicationContext()
+ storage_manager = cast(
+ "android.os.storage.StorageManager",
+ context.getSystemService(Context.STORAGE_SERVICE),
+ )
+
+ if storage_manager is not None:
+ if SDK_INT >= 24:
+ storage_volumes = storage_manager.getStorageVolumes()
+ for storage_volume in storage_volumes:
+ if storage_volume.isRemovable():
+ try:
+ directory = storage_volume.getDirectory()
+ except AttributeError:
+ directory = storage_volume.getPathFile()
+ path = directory.getAbsolutePath()
+ else:
+ storage_volumes = storage_manager.getVolumeList()
+ for storage_volume in storage_volumes:
+ if storage_volume.isRemovable():
+ path = storage_volume.getPath()
+
return path
def _get_root_dir(self):

diff --git a/sbapp/plyer/platforms/ios/accelerometer.py b/sbapp/plyer/platforms/ios/accelerometer.py
index 72503843..0f61f2b9 100644
--- a/sbapp/plyer/platforms/ios/accelerometer.py
+++ b/sbapp/plyer/platforms/ios/accelerometer.py
@@ -6,7 +6,7 @@ Taken from: http://pyobjus.readthedocs.org/en/latest/pyobjus_ios.html \
#accessing-accelerometer
'''
-from plyer.facades import Accelerometer
+from sbapp.plyer.facades import Accelerometer
from pyobjus import autoclass

diff --git a/sbapp/plyer/platforms/ios/barometer.py b/sbapp/plyer/platforms/ios/barometer.py
index a5c5dc34..9ec8d6e5 100644
--- a/sbapp/plyer/platforms/ios/barometer.py
+++ b/sbapp/plyer/platforms/ios/barometer.py
@@ -3,7 +3,7 @@ iOS Barometer
-------------
'''
-from plyer.facades import Barometer
+from sbapp.plyer.facades import Barometer
from pyobjus import autoclass

diff --git a/sbapp/plyer/platforms/ios/battery.py b/sbapp/plyer/platforms/ios/battery.py
index 818f0be4..d7fa5d81 100644
--- a/sbapp/plyer/platforms/ios/battery.py
+++ b/sbapp/plyer/platforms/ios/battery.py
@@ -4,7 +4,7 @@ Module of iOS API for plyer.battery.
from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework
-from plyer.facades import Battery
+from sbapp.plyer.facades import Battery
load_framework('/System/Library/Frameworks/UIKit.framework')
UIDevice = autoclass('UIDevice')

diff --git a/sbapp/plyer/platforms/ios/brightness.py b/sbapp/plyer/platforms/ios/brightness.py
index 065869a4..f4a70f4e 100644
--- a/sbapp/plyer/platforms/ios/brightness.py
+++ b/sbapp/plyer/platforms/ios/brightness.py
@@ -4,7 +4,7 @@ iOS Brightness
'''
from pyobjus import autoclass
-from plyer.facades import Brightness
+from sbapp.plyer.facades import Brightness
from pyobjus.dylib_manager import load_framework
load_framework('/System/Library/Frameworks/UIKit.framework')

diff --git a/sbapp/plyer/platforms/ios/call.py b/sbapp/plyer/platforms/ios/call.py
index c7513993..9999d6aa 100644
--- a/sbapp/plyer/platforms/ios/call.py
+++ b/sbapp/plyer/platforms/ios/call.py
@@ -3,7 +3,7 @@ IOS Call
----------
'''
-from plyer.facades import Call
+from sbapp.plyer.facades import Call
from pyobjus import autoclass, objc_str
NSURL = autoclass('NSURL')

diff --git a/sbapp/plyer/platforms/ios/camera.py b/sbapp/plyer/platforms/ios/camera.py
index dfe5265b..a61bd96c 100644
--- a/sbapp/plyer/platforms/ios/camera.py
+++ b/sbapp/plyer/platforms/ios/camera.py
@@ -1,7 +1,7 @@
from os import remove
-from plyer.facades import Camera
+from sbapp.plyer.facades import Camera
-from plyer.utils import reify
+from sbapp.plyer.utils import reify
class iOSCamera(Camera):
@@ -14,7 +14,7 @@ class iOSCamera(Camera):
return PhotosLibrary()
def _take_picture(self, on_complete, filename=None):
- assert(on_complete is not None)
+ assert on_complete is not None
self.on_complete = on_complete
self.filename = filename
photos = self.photos
@@ -38,7 +38,7 @@ class iOSCamera(Camera):
self._remove(self.filename)
def _take_video(self, on_complete, filename=None):
- assert(on_complete is not None)
+ assert on_complete is not None
raise NotImplementedError
def _remove(self, fn):

diff --git a/sbapp/plyer/platforms/ios/compass.py b/sbapp/plyer/platforms/ios/compass.py
index 855484b3..a5c865fb 100644
--- a/sbapp/plyer/platforms/ios/compass.py
+++ b/sbapp/plyer/platforms/ios/compass.py
@@ -3,7 +3,7 @@ iOS Compass
-----------
'''
-from plyer.facades import Compass
+from sbapp.plyer.facades import Compass
from pyobjus import autoclass

diff --git a/sbapp/plyer/platforms/ios/email.py b/sbapp/plyer/platforms/ios/email.py
index e1cc7cbc..ec54860e 100644
--- a/sbapp/plyer/platforms/ios/email.py
+++ b/sbapp/plyer/platforms/ios/email.py
@@ -7,7 +7,7 @@ try:
except ImportError:
from urllib import quote
-from plyer.facades import Email
+from sbapp.plyer.facades import Email
from pyobjus import autoclass, objc_str
from pyobjus.dylib_manager import load_framework

diff --git a/sbapp/plyer/platforms/ios/filechooser.py b/sbapp/plyer/platforms/ios/filechooser.py
index ba49ba96..0320d6c5 100644
--- a/sbapp/plyer/platforms/ios/filechooser.py
+++ b/sbapp/plyer/platforms/ios/filechooser.py
@@ -7,7 +7,7 @@ This module houses the iOS implementation of the plyer FileChooser.
.. versionadded:: 1.4.4
'''
-from plyer.facades import FileChooser
+from sbapp.plyer.facades import FileChooser
from pyobjus import autoclass, protocol
from pyobjus.dylib_manager import load_framework

diff --git a/sbapp/plyer/platforms/ios/flash.py b/sbapp/plyer/platforms/ios/flash.py
index e4bedac0..17f59f12 100644
--- a/sbapp/plyer/platforms/ios/flash.py
+++ b/sbapp/plyer/platforms/ios/flash.py
@@ -3,7 +3,7 @@
Flash
-----
"""
-from plyer.facades import Flash
+from sbapp.plyer.facades import Flash
from pyobjus import autoclass
NSString = autoclass("NSString")

diff --git a/sbapp/plyer/platforms/ios/gps.py b/sbapp/plyer/platforms/ios/gps.py
index 4c52738d..d7ee0a72 100644
--- a/sbapp/plyer/platforms/ios/gps.py
+++ b/sbapp/plyer/platforms/ios/gps.py
@@ -5,7 +5,7 @@ iOS GPS
from pyobjus import autoclass, protocol
from pyobjus.dylib_manager import load_framework
-from plyer.facades import GPS
+from sbapp.plyer.facades import GPS
load_framework('/System/Library/Frameworks/CoreLocation.framework')
CLLocationManager = autoclass('CLLocationManager')

diff --git a/sbapp/plyer/platforms/ios/gravity.py b/sbapp/plyer/platforms/ios/gravity.py
index a2b1be0c..9452bdac 100644
--- a/sbapp/plyer/platforms/ios/gravity.py
+++ b/sbapp/plyer/platforms/ios/gravity.py
@@ -4,7 +4,7 @@ iOS Gravity
'''
-from plyer.facades import Gravity
+from sbapp.plyer.facades import Gravity
from pyobjus import autoclass

diff --git a/sbapp/plyer/platforms/ios/gyroscope.py b/sbapp/plyer/platforms/ios/gyroscope.py
index 340653c8..367e1bfc 100644
--- a/sbapp/plyer/platforms/ios/gyroscope.py
+++ b/sbapp/plyer/platforms/ios/gyroscope.py
@@ -3,7 +3,7 @@ iOS Gyroscope
---------------------
'''
-from plyer.facades import Gyroscope
+from sbapp.plyer.facades import Gyroscope
from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework

diff --git a/sbapp/plyer/platforms/ios/keystore.py b/sbapp/plyer/platforms/ios/keystore.py
index 289a5c01..a18cde27 100644
--- a/sbapp/plyer/platforms/ios/keystore.py
+++ b/sbapp/plyer/platforms/ios/keystore.py
@@ -1,4 +1,4 @@
-from plyer.facades import Keystore
+from sbapp.plyer.facades import Keystore
from pyobjus import autoclass, objc_str
NSUserDefaults = autoclass('NSUserDefaults')

diff --git a/sbapp/plyer/platforms/ios/maps.py b/sbapp/plyer/platforms/ios/maps.py
new file mode 100644
index 00000000..dac01d5d
--- /dev/null
+++ b/sbapp/plyer/platforms/ios/maps.py
@@ -0,0 +1,78 @@
+'''
+Module of iOS API for plyer.maps.
+'''
+
+import webbrowser
+from sbapp.plyer.facades import Maps
+from urllib.parse import quote_plus
+
+
+class iOSMaps(Maps):
+ '''
+ Implementation of iOS Maps API.
+ '''
+
+ def _open_by_address(self, address, **kwargs):
+ '''
+ :param address: An address string that geolocation can understand.
+ '''
+
+ address = quote_plus(address, safe=',')
+ maps_address = 'http://maps.apple.com/?address=' + address
+
+ webbrowser.open(maps_address)
+
+ def _open_by_lat_long(self, latitude, longitude, **kwargs):
+ '''
+ Open a coordinate span denoting a latitudinal delta and a
+ longitudinal delta (similar to MKCoordinateSpan)
+
+ :param name: (optional), will set the name of the dropped pin
+ '''
+
+ name = kwargs.get("name", "Selected Location")
+ maps_address = 'http://maps.apple.com/?ll={},{}&q={}'.format(
+ latitude, longitude, name)
+
+ webbrowser.open(maps_address)
+
+ def _search(self, query, **kwargs):
+ '''
+ :param query: A string that describes the search object (ex. "Pizza")
+
+ :param latitude: (optional), narrow down query within area,
+ MUST BE USED WITH LONGITUDE
+
+ :param longitude: (optional), narrow down query within area,
+ MUST BE USED WITH LATITUDE
+ '''
+
+ latitude = kwargs.get('latitude')
+ longitude = kwargs.get('longitude')
+
+ query = quote_plus(query, safe=',')
+ maps_address = 'http://maps.apple.com/?q=' + query
+
+ if latitude is not None and longitude is not None:
+ maps_address += '&sll={},{}'.format(latitude, longitude)
+
+ webbrowser.open(maps_address)
+
+ def _route(self, saddr, daddr, **kwargs):
+ '''
+ :param saddr: can be given as 'address' or 'lat,long'
+ :param daddr: can be given as 'address' or 'lat,long'
+ '''
+ saddr = quote_plus(saddr, safe=',')
+ daddr = quote_plus(daddr, safe=',')
+
+ maps_address = 'http://maps.apple.com/?saddr={}&daddr={}'.format(
+ saddr, daddr)
+ webbrowser.open(maps_address)
+
+
+def instance():
+ '''
+ Instance for facade proxy.
+ '''
+ return iOSMaps()

diff --git a/sbapp/plyer/platforms/ios/sms.py b/sbapp/plyer/platforms/ios/sms.py
index ef3acaa7..fc6b1b41 100644
--- a/sbapp/plyer/platforms/ios/sms.py
+++ b/sbapp/plyer/platforms/ios/sms.py
@@ -3,7 +3,7 @@ IOS Sms
----------
'''
-from plyer.facades import Sms
+from sbapp.plyer.facades import Sms
from pyobjus import autoclass, objc_str
from pyobjus.dylib_manager import load_framework

diff --git a/sbapp/plyer/platforms/ios/spatialorientation.py b/sbapp/plyer/platforms/ios/spatialorientation.py
index d42d0fa0..7c6d9ccc 100644
--- a/sbapp/plyer/platforms/ios/spatialorientation.py
+++ b/sbapp/plyer/platforms/ios/spatialorientation.py
@@ -4,7 +4,7 @@ iOS Spatial Orientation
'''
-from plyer.facades import SpatialOrientation
+from sbapp.plyer.facades import SpatialOrientation
from pyobjus import autoclass

diff --git a/sbapp/plyer/platforms/ios/storagepath.py b/sbapp/plyer/platforms/ios/storagepath.py
index cd8bbc39..c69a083a 100644
--- a/sbapp/plyer/platforms/ios/storagepath.py
+++ b/sbapp/plyer/platforms/ios/storagepath.py
@@ -3,7 +3,7 @@ iOS Storage Path
--------------------
'''
-from plyer.facades import StoragePath
+from sbapp.plyer.facades import StoragePath
from pyobjus import autoclass
import os

diff --git a/sbapp/plyer/platforms/ios/tts.py b/sbapp/plyer/platforms/ios/tts.py
index 046769f6..510399d1 100644
--- a/sbapp/plyer/platforms/ios/tts.py
+++ b/sbapp/plyer/platforms/ios/tts.py
@@ -1,7 +1,7 @@
from pyobjus import autoclass, objc_str
from pyobjus.dylib_manager import load_framework
-from plyer.facades import TTS
+from sbapp.plyer.facades import TTS
load_framework('/System/Library/Frameworks/AVFoundation.framework')
AVSpeechUtterance = autoclass('AVSpeechUtterance')
@@ -23,7 +23,7 @@ class iOSTextToSpeech(TTS):
def _speak(self, **kwargs):
message = kwargs.get('message')
- if(not self.voice):
+ if not self.voice:
self._set_locale()
utterance = \

diff --git a/sbapp/plyer/platforms/ios/uniqueid.py b/sbapp/plyer/platforms/ios/uniqueid.py
index 4d302499..e9fa8154 100644
--- a/sbapp/plyer/platforms/ios/uniqueid.py
+++ b/sbapp/plyer/platforms/ios/uniqueid.py
@@ -4,7 +4,7 @@ Module of iOS API for plyer.uniqueid.
from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework
-from plyer.facades import UniqueID
+from sbapp.plyer.facades import UniqueID
load_framework('/System/Library/Frameworks/UIKit.framework')
UIDevice = autoclass('UIDevice')

diff --git a/sbapp/plyer/platforms/ios/vibrator.py b/sbapp/plyer/platforms/ios/vibrator.py
index 922182cd..05c12c53 100644
--- a/sbapp/plyer/platforms/ios/vibrator.py
+++ b/sbapp/plyer/platforms/ios/vibrator.py
@@ -4,7 +4,7 @@ Install: Add AudioToolbox framework to your application.
'''
import ctypes
-from plyer.facades import Vibrator
+from sbapp.plyer.facades import Vibrator
class IosVibrator(Vibrator):

diff --git a/sbapp/plyer/platforms/linux/accelerometer.py b/sbapp/plyer/platforms/linux/accelerometer.py
index 7af283b5..889de15b 100644
--- a/sbapp/plyer/platforms/linux/accelerometer.py
+++ b/sbapp/plyer/platforms/linux/accelerometer.py
@@ -3,7 +3,7 @@ Linux accelerometer
---------------------
'''
-from plyer.facades import Accelerometer
+from sbapp.plyer.facades import Accelerometer
import glob
import re

diff --git a/sbapp/plyer/platforms/linux/audio.py b/sbapp/plyer/platforms/linux/audio.py
new file mode 100644
index 00000000..55cdb7cb
--- /dev/null
+++ b/sbapp/plyer/platforms/linux/audio.py
@@ -0,0 +1,48 @@
+import time
+import threading
+from sbapp.plyer.facades.audio import Audio
+
+class LinuxAudio(Audio):
+
+ def __init__(self, file_path=None):
+ default_path = None
+ super().__init__(file_path or default_path)
+
+ self._recorder = None
+ self._player = None
+ self._check_thread = None
+ self._finished_callback = None
+
+ def _check_playback(self):
+ while self.is_playing:
+ time.sleep(0.25)
+
+ if self._finished_callback and callable(self._finished_callback):
+ self._check_thread = None
+ self._finished_callback(self)
+
+ def _start(self):
+ # TODO: Implement recording
+ pass
+
+ def _stop(self):
+ # TODO: Implement recording
+ pass
+
+ def _play(self):
+ # TODO: Implement playback
+ self.is_playing = True
+
+ self._check_thread = threading.Thread(target=self._check_playback, daemon=True)
+ self._check_thread.start()
+
+ def fauxplay():
+ time.sleep(1.5)
+ self.is_playing = False
+
+ threading.Thread(target=fauxplay, daemon=True).start()
+
+
+
+def instance():
+ return LinuxAudio()

diff --git a/sbapp/plyer/platforms/linux/battery.py b/sbapp/plyer/platforms/linux/battery.py
index b0c46919..9fc9f9ff 100644
--- a/sbapp/plyer/platforms/linux/battery.py
+++ b/sbapp/plyer/platforms/linux/battery.py
@@ -2,13 +2,12 @@
Module of Linux API for plyer.battery.
'''
-import os
from math import floor
from os import environ
from os.path import exists, join
from subprocess import Popen, PIPE
-from plyer.facades import Battery
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import Battery
+from sbapp.plyer.utils import whereis_exe
class LinuxBattery(Battery):
@@ -20,10 +19,10 @@ class LinuxBattery(Battery):
def _get_state(self):
status = {"isCharging": None, "percentage": None}
- kernel_bat_path = join('/sys', 'class', 'power_supply', self.node_name)
+ kernel_bat_path = join('/sys', 'class', 'power_supply', 'BAT0')
uevent = join(kernel_bat_path, 'uevent')
- with open(uevent, "rb") as fle:
+ with open(uevent) as fle:
lines = [
line.decode('utf-8').strip()
for line in fle.readlines()
@@ -34,34 +33,70 @@ class LinuxBattery(Battery):
}
is_charging = output['POWER_SUPPLY_STATUS'] == 'Charging'
- charge_percent = float(output['POWER_SUPPLY_CAPACITY'])
+ total = float(output['POWER_SUPPLY_CHARGE_FULL'])
+ now = float(output['POWER_SUPPLY_CHARGE_NOW'])
- status['percentage'] = charge_percent
+ capacity = floor(now / total * 100)
+
+ status['percentage'] = capacity
status['isCharging'] = is_charging
return status
+class UPowerBattery(Battery):
+ '''
+ Implementation of UPower battery API.
+ '''
+
+ def _get_state(self):
+ # if no LANG specified, return empty string
+ old_lang = environ.get('LANG', '')
+ environ['LANG'] = 'C'
+ status = {"isCharging": None, "percentage": None}
+
+ # We are supporting only one battery now
+ # this will fail if there is no object with such path,
+ # however it's safer than 'upower -d' which provides
+ # multiple unrelated 'state' and 'percentage' keywords
+ dev = "/org/freedesktop/UPower/devices/battery_BAT0"
+ upower_process = Popen(
+ ["upower", "--show-info", dev],
+ stdout=PIPE
+ )
+ output = upower_process.communicate()[0].decode()
+ environ['LANG'] = old_lang
+ if not output:
+ return status
+ state = percentage = None
+
+ for line in output.splitlines():
+ if 'state' in line:
+ state = line.rpartition(':')[-1].strip()
+
+ if 'percentage' in line:
+ percentage = line.rpartition(':')[-1].strip()[:-1]
+
+ # switching decimal comma to dot
+ # (different LC_NUMERIC locale)
+ percentage = float(
+ percentage.replace(',', '.')
+ )
+
+ if state:
+ status['isCharging'] = state == "charging"
+ status['percentage'] = percentage
+ return status
+
+
def instance():
'''
Instance for facade proxy.
'''
import sys
- # if whereis_exe('upower'):
- # return UPowerBattery()
- # sys.stderr.write("upower not found.")
-
- node_exists = False
- bn = 0
- node_name = None
- for bi in range(0,10):
- path = join('/sys', 'class', 'power_supply', 'BAT'+str(bi))
- if os.path.isdir(path):
- node_name = "BAT"+str(bi)
- break
-
- if node_name:
- b = LinuxBattery()
- b.node_name = node_name
- return b
+ if whereis_exe('upower'):
+ return UPowerBattery()
+ sys.stderr.write("upower not found.")
+ if exists(join('/sys', 'class', 'power_supply', 'BAT0')):
+ return LinuxBattery()
return Battery()

diff --git a/sbapp/plyer/platforms/linux/brightness.py b/sbapp/plyer/platforms/linux/brightness.py
index 0de16985..eb30536f 100755
--- a/sbapp/plyer/platforms/linux/brightness.py
+++ b/sbapp/plyer/platforms/linux/brightness.py
@@ -4,7 +4,7 @@ Linux Brightness
'''
-from plyer.facades import Brightness
+from sbapp.plyer.facades import Brightness
import subprocess
import os

diff --git a/sbapp/plyer/platforms/linux/cpu.py b/sbapp/plyer/platforms/linux/cpu.py
index 049a2d4a..66a532f4 100644
--- a/sbapp/plyer/platforms/linux/cpu.py
+++ b/sbapp/plyer/platforms/linux/cpu.py
@@ -5,8 +5,8 @@ Module of Linux API for plyer.cpu.
from os.path import join
from os import environ, listdir
from subprocess import Popen, PIPE
-from plyer.facades import CPU
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import CPU
+from sbapp.plyer.utils import whereis_exe
class LinuxCPU(CPU):

diff --git a/sbapp/plyer/platforms/linux/devicename.py b/sbapp/plyer/platforms/linux/devicename.py
index 89413791..cbd90080 100644
--- a/sbapp/plyer/platforms/linux/devicename.py
+++ b/sbapp/plyer/platforms/linux/devicename.py
@@ -3,7 +3,7 @@ Module of Linux API for plyer.devicename.
'''
import socket
-from plyer.facades import DeviceName
+from sbapp.plyer.facades import DeviceName
class LinuxDeviceName(DeviceName):

diff --git a/sbapp/plyer/platforms/linux/email.py b/sbapp/plyer/platforms/linux/email.py
index 64cbb6d5..90efce95 100644
--- a/sbapp/plyer/platforms/linux/email.py
+++ b/sbapp/plyer/platforms/linux/email.py
@@ -7,8 +7,8 @@ try:
from urllib.parse import quote
except ImportError:
from urllib import quote
-from plyer.facades import Email
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import Email
+from sbapp.plyer.utils import whereis_exe
class LinuxEmail(Email):

diff --git a/sbapp/plyer/platforms/linux/filechooser.py b/sbapp/plyer/platforms/linux/filechooser.py
index cfdb1ae9..a8e2ef2a 100644
--- a/sbapp/plyer/platforms/linux/filechooser.py
+++ b/sbapp/plyer/platforms/linux/filechooser.py
@@ -3,7 +3,7 @@ Linux file chooser
------------------
'''
-from plyer.facades import FileChooser
+from sbapp.plyer.facades import FileChooser
from distutils.spawn import find_executable as which
import os
import subprocess as sp
@@ -122,7 +122,7 @@ class ZenityFileChooser(SubprocessFileChooser):
if self.icon:
cmdline += ["--window-icon", self.icon]
for f in self.filters:
- if type(f) == str:
+ if isinstance(f, str):
cmdline += ["--file-filter", f]
else:
cmdline += [
@@ -150,7 +150,7 @@ class KDialogFileChooser(SubprocessFileChooser):
filt = []
for f in self.filters:
- if type(f) == str:
+ if isinstance(f, str):
filt += [f]
else:
filt += list(f[1:])
@@ -195,7 +195,7 @@ class YADFileChooser(SubprocessFileChooser):
def _gen_cmdline(self):
cmdline = [
which(self.executable),
- "--file-selection",
+ "--file",
"--confirm-overwrite",
"--geometry",
"800x600+150+150"
@@ -215,7 +215,7 @@ class YADFileChooser(SubprocessFileChooser):
if self.icon:
cmdline += ["--window-icon", self.icon]
for f in self.filters:
- if type(f) == str:
+ if isinstance(f, str):
cmdline += ["--file-filter", f]
else:
cmdline += [

diff --git a/sbapp/plyer/platforms/linux/keystore.py b/sbapp/plyer/platforms/linux/keystore.py
index 105ebd31..b43be70a 100644
--- a/sbapp/plyer/platforms/linux/keystore.py
+++ b/sbapp/plyer/platforms/linux/keystore.py
@@ -3,7 +3,7 @@ try:
except ImportError:
raise NotImplementedError()
-from plyer.facades import Keystore
+from sbapp.plyer.facades import Keystore
class LinuxKeystore(Keystore):

diff --git a/sbapp/plyer/platforms/linux/notification.py b/sbapp/plyer/platforms/linux/notification.py
index 95a6472a..65f5131d 100644
--- a/sbapp/plyer/platforms/linux/notification.py
+++ b/sbapp/plyer/platforms/linux/notification.py
@@ -4,8 +4,8 @@ Module of Linux API for plyer.notification.
import warnings
import subprocess
-from plyer.facades import Notification
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import Notification
+from sbapp.plyer.utils import whereis_exe
import os

diff --git a/sbapp/plyer/platforms/linux/orientation.py b/sbapp/plyer/platforms/linux/orientation.py
index e60fa42b..c1901284 100644
--- a/sbapp/plyer/platforms/linux/orientation.py
+++ b/sbapp/plyer/platforms/linux/orientation.py
@@ -1,5 +1,5 @@
import subprocess as sb
-from plyer.facades import Orientation
+from sbapp.plyer.facades import Orientation
class LinuxOrientation(Orientation):

diff --git a/sbapp/plyer/platforms/linux/processors.py b/sbapp/plyer/platforms/linux/processors.py
index 74bb73af..332ec83d 100644
--- a/sbapp/plyer/platforms/linux/processors.py
+++ b/sbapp/plyer/platforms/linux/processors.py
@@ -1,6 +1,6 @@
from subprocess import Popen, PIPE
-from plyer.facades import Processors
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import Processors
+from sbapp.plyer.utils import whereis_exe
from os import environ

diff --git a/sbapp/plyer/platforms/linux/screenshot.py b/sbapp/plyer/platforms/linux/screenshot.py
index 00eeb5c5..5dc2ccfc 100644
--- a/sbapp/plyer/platforms/linux/screenshot.py
+++ b/sbapp/plyer/platforms/linux/screenshot.py
@@ -1,8 +1,8 @@
import subprocess
from os.path import join
-from plyer.facades import Screenshot
-from plyer.utils import whereis_exe
-from plyer.platforms.linux.storagepath import LinuxStoragePath
+from sbapp.plyer.facades import Screenshot
+from sbapp.plyer.utils import whereis_exe
+from sbapp.plyer.platforms.linux.storagepath import LinuxStoragePath
class LinuxScreenshot(Screenshot):

diff --git a/sbapp/plyer/platforms/linux/storagepath.py b/sbapp/plyer/platforms/linux/storagepath.py
index 736674a2..b32a5fed 100755
--- a/sbapp/plyer/platforms/linux/storagepath.py
+++ b/sbapp/plyer/platforms/linux/storagepath.py
@@ -3,7 +3,7 @@ Linux Storage Path
--------------------
'''
-from plyer.facades import StoragePath
+from sbapp.plyer.facades import StoragePath
from os.path import expanduser, dirname, abspath, join, exists
# Default paths for each name

diff --git a/sbapp/plyer/platforms/linux/tts.py b/sbapp/plyer/platforms/linux/tts.py
index 39327725..3b8b634b 100644
--- a/sbapp/plyer/platforms/linux/tts.py
+++ b/sbapp/plyer/platforms/linux/tts.py
@@ -1,6 +1,6 @@
import subprocess
-from plyer.facades import TTS
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import TTS
+from sbapp.plyer.utils import whereis_exe
class EspeakTextToSpeech(TTS):

diff --git a/sbapp/plyer/platforms/linux/uniqueid.py b/sbapp/plyer/platforms/linux/uniqueid.py
index 44926b1c..30c11121 100644
--- a/sbapp/plyer/platforms/linux/uniqueid.py
+++ b/sbapp/plyer/platforms/linux/uniqueid.py
@@ -4,8 +4,8 @@ Module of Linux API for plyer.uniqueid.
from os import environ
from subprocess import Popen, PIPE
-from plyer.facades import UniqueID
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import UniqueID
+from sbapp.plyer.utils import whereis_exe
class LinuxUniqueID(UniqueID):

diff --git a/sbapp/plyer/platforms/linux/wifi.py b/sbapp/plyer/platforms/linux/wifi.py
index 73f09b66..144f58ac 100644
--- a/sbapp/plyer/platforms/linux/wifi.py
+++ b/sbapp/plyer/platforms/linux/wifi.py
@@ -6,8 +6,8 @@
'''
from subprocess import Popen, PIPE, call
-from plyer.facades import Wifi
-from plyer.utils import whereis_exe, deprecated
+from sbapp.plyer.facades import Wifi
+from sbapp.plyer.utils import whereis_exe, deprecated
try:
import wifi

diff --git a/sbapp/plyer/platforms/macosx/accelerometer.py b/sbapp/plyer/platforms/macosx/accelerometer.py
index 91e72c37..5b52578f 100644
--- a/sbapp/plyer/platforms/macosx/accelerometer.py
+++ b/sbapp/plyer/platforms/macosx/accelerometer.py
@@ -3,8 +3,8 @@ MacOSX accelerometer
---------------------
'''
-from plyer.facades import Accelerometer
-from plyer.platforms.macosx.libs import osx_motion_sensor
+from sbapp.plyer.facades import Accelerometer
+from sbapp.plyer.platforms.macosx.libs import osx_motion_sensor
class OSXAccelerometer(Accelerometer):

diff --git a/sbapp/plyer/platforms/macosx/audio.py b/sbapp/plyer/platforms/macosx/audio.py
index 3ab9ce12..a9f4237f 100644
--- a/sbapp/plyer/platforms/macosx/audio.py
+++ b/sbapp/plyer/platforms/macosx/audio.py
@@ -3,8 +3,8 @@ from os.path import join
from pyobjus import autoclass
from pyobjus.dylib_manager import INCLUDE, load_framework
-from plyer.facades import Audio
-from plyer.platforms.macosx.storagepath import OSXStoragePath
+from sbapp.plyer.facades import Audio
+from sbapp.plyer.platforms.macosx.storagepath import OSXStoragePath
load_framework(INCLUDE.Foundation)
load_framework(INCLUDE.AVFoundation)
@@ -19,16 +19,24 @@ NSError = autoclass('NSError').alloc()
class OSXAudio(Audio):
def __init__(self, file_path=None):
- default_path = join(
- OSXStoragePath().get_music_dir(),
- 'audio.wav'
- )
+ default_path = None
super().__init__(file_path or default_path)
self._recorder = None
self._player = None
self._current_file = None
+ self._check_thread = None
+ self._finished_callback = None
+
+ def _check_playback(self):
+ while self._player and self._player.isPlaying:
+ time.sleep(0.25)
+
+ if self._finished_callback and callable(self._finished_callback):
+ self._check_thread = None
+ self._finished_callback(self)
+
def _start(self):
# Conversion of Python file path string to Objective-C NSString
file_path_NSString = NSString.alloc()
@@ -44,7 +52,7 @@ class OSXAudio(Audio):
# Internal audio file format specification
af = AVAudioFormat.alloc()
af = af.initWithCommonFormat_sampleRate_channels_interleaved_(
- 1, 44100.0, 2, True
+ 1, 44100.0, 1, True
)
# Audio recorder instance initialization with specified file NSURL
@@ -85,6 +93,9 @@ class OSXAudio(Audio):
self._player.play()
+ self._check_thread = threading.Thread(target=self._check_playback, daemon=True)
+ self._check_thread.start()
+
def instance():
return OSXAudio()

diff --git a/sbapp/plyer/platforms/macosx/battery.py b/sbapp/plyer/platforms/macosx/battery.py
index 57965d32..ec424860 100644
--- a/sbapp/plyer/platforms/macosx/battery.py
+++ b/sbapp/plyer/platforms/macosx/battery.py
@@ -4,8 +4,8 @@ Module of MacOS API for plyer.battery.
from os import environ
from subprocess import Popen, PIPE
-from plyer.facades import Battery
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import Battery
+from sbapp.plyer.utils import whereis_exe
class OSXBattery(Battery):

diff --git a/sbapp/plyer/platforms/macosx/bluetooth.py b/sbapp/plyer/platforms/macosx/bluetooth.py
index 25758177..7c2ec225 100644
--- a/sbapp/plyer/platforms/macosx/bluetooth.py
+++ b/sbapp/plyer/platforms/macosx/bluetooth.py
@@ -3,8 +3,8 @@ Module of MacOS API for plyer.bluetooth.
'''
from subprocess import Popen, PIPE
-from plyer.facades import Bluetooth
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import Bluetooth
+from sbapp.plyer.utils import whereis_exe
from os import environ

diff --git a/sbapp/plyer/platforms/macosx/cpu.py b/sbapp/plyer/platforms/macosx/cpu.py
index 7b7da7fe..26eeebbd 100644
--- a/sbapp/plyer/platforms/macosx/cpu.py
+++ b/sbapp/plyer/platforms/macosx/cpu.py
@@ -3,8 +3,8 @@ Module of MacOS API for plyer.cpu.
'''
from subprocess import Popen, PIPE
-from plyer.facades import CPU
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import CPU
+from sbapp.plyer.utils import whereis_exe
class OSXCPU(CPU):

diff --git a/sbapp/plyer/platforms/macosx/devicename.py b/sbapp/plyer/platforms/macosx/devicename.py
index 6652425e..e100246f 100644
--- a/sbapp/plyer/platforms/macosx/devicename.py
+++ b/sbapp/plyer/platforms/macosx/devicename.py
@@ -3,7 +3,7 @@ Module of MacOSX API for plyer.devicename.
'''
import socket
-from plyer.facades import DeviceName
+from sbapp.plyer.facades import DeviceName
class OSXDeviceName(DeviceName):

diff --git a/sbapp/plyer/platforms/macosx/email.py b/sbapp/plyer/platforms/macosx/email.py
index 49c5d4dd..42295ad0 100644
--- a/sbapp/plyer/platforms/macosx/email.py
+++ b/sbapp/plyer/platforms/macosx/email.py
@@ -9,8 +9,8 @@ try:
except ImportError:
from urllib import quote
-from plyer.facades import Email
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import Email
+from sbapp.plyer.utils import whereis_exe
class MacOSXEmail(Email):

diff --git a/sbapp/plyer/platforms/macosx/filechooser.py b/sbapp/plyer/platforms/macosx/filechooser.py
index 85fb91c7..1d480621 100644
--- a/sbapp/plyer/platforms/macosx/filechooser.py
+++ b/sbapp/plyer/platforms/macosx/filechooser.py
@@ -3,7 +3,7 @@ Mac OS X file chooser
---------------------
'''
-from plyer.facades import FileChooser
+from sbapp.plyer.facades import FileChooser
from pyobjus import autoclass, objc_arr, objc_str
from pyobjus.dylib_manager import load_framework, INCLUDE
@@ -80,7 +80,7 @@ class MacFileChooser:
if self.filters:
filthies = []
for f in self.filters:
- if type(f) == str:
+ if isinstance(f, str):
f = (None, f)
for s in f[1:]:
if not self.use_extensions:

diff --git a/sbapp/plyer/platforms/macosx/keystore.py b/sbapp/plyer/platforms/macosx/keystore.py
index 11e60e89..ec00cff6 100644
--- a/sbapp/plyer/platforms/macosx/keystore.py
+++ b/sbapp/plyer/platforms/macosx/keystore.py
@@ -3,7 +3,7 @@ try:
except ImportError:
raise NotImplementedError()
-from plyer.facades import Keystore
+from sbapp.plyer.facades import Keystore
class OSXKeystore(Keystore):

diff --git a/sbapp/plyer/platforms/macosx/libs/osx_motion_sensor.py b/sbapp/plyer/platforms/macosx/libs/osx_motion_sensor.py
index d49df433..a5dc76af 100644
--- a/sbapp/plyer/platforms/macosx/libs/osx_motion_sensor.py
+++ b/sbapp/plyer/platforms/macosx/libs/osx_motion_sensor.py
@@ -86,7 +86,7 @@ def read_sms():
inStructure = data_structure()
outStructure = data_structure()
- if(is_os_64bit() or hasattr(IOKit, 'IOConnectCallStructMethod')):
+ if is_os_64bit() or hasattr(IOKit, 'IOConnectCallStructMethod'):
structureInSize = IOItemCount(sizeof(data_structure))
structureOutSize = c_size_t(sizeof(data_structure))
@@ -120,7 +120,7 @@ def get_coord():
ret, data = read_sms()
if (ret > 0):
- if(data.x):
+ if data.x:
return (data.x, data.y, data.z)
else:
return (None, None, None)

diff --git a/sbapp/plyer/platforms/macosx/maps.py b/sbapp/plyer/platforms/macosx/maps.py
new file mode 100644
index 00000000..7a6d998a
--- /dev/null
+++ b/sbapp/plyer/platforms/macosx/maps.py
@@ -0,0 +1,90 @@
+'''
+Module of macOS API for plyer.maps.
+'''
+
+from subprocess import Popen, PIPE
+from sbapp.plyer.facades import Maps
+from urllib.parse import quote_plus
+
+
+class MacOSMaps(Maps):
+ '''
+ Implementation of MacOS Maps API.
+ '''
+
+ def _open_by_address(self, address, **kwargs):
+ '''
+ :param address: An address string that geolocation can understand.
+ '''
+
+ address = quote_plus(address, safe=',')
+ maps_address = 'http://maps.apple.com/?address=' + address
+
+ process = Popen(
+ ['open', '-a', 'Maps', maps_address],
+ stdout=PIPE, stderr=PIPE)
+ stdout, stderr = process.communicate()
+
+ def _open_by_lat_long(self, latitude, longitude, **kwargs):
+ '''
+ Open a coordinate span denoting a latitudinal delta and a
+ longitudinal delta (similar to MKCoordinateSpan)
+
+ :param name: (optional), will set the name of the dropped pin
+ '''
+
+ name = kwargs.get("name", "Selected Location")
+ maps_address = 'http://maps.apple.com/?ll={},{}&q={}'.format(
+ latitude, longitude, name)
+
+ process = Popen(
+ ['open', '-a', 'Maps', maps_address],
+ stdout=PIPE, stderr=PIPE)
+ stdout, stderr = process.communicate()
+
+ def _search(self, query, **kwargs):
+ '''
+ :param query: A string that describes the search object (ex. "Pizza")
+
+ :param latitude: (optional), narrow down query within area,
+ MUST BE USED WITH LONGITUDE
+
+ :param longitude: (optional), narrow down query within area,
+ MUST BE USED WITH LATITUDE
+ '''
+
+ latitude = kwargs.get('latitude')
+ longitude = kwargs.get('longitude')
+
+ query = quote_plus(query, safe=',')
+ maps_address = 'http://maps.apple.com/?q=' + query
+
+ if latitude is not None and longitude is not None:
+ maps_address += '&sll={},{}'.format(latitude, longitude)
+
+ process = Popen(
+ ['open', '-a', 'Maps', maps_address],
+ stdout=PIPE, stderr=PIPE)
+ stdout, stderr = process.communicate()
+
+ def _route(self, saddr, daddr, **kwargs):
+ '''
+ :param saddr: can be given as 'address' or 'lat,long'
+ :param daddr: can be given as 'address' or 'lat,long'
+ '''
+ saddr = quote_plus(saddr, safe=',')
+ daddr = quote_plus(daddr, safe=',')
+
+ maps_address = 'http://maps.apple.com/?saddr={}&daddr={}'.format(
+ saddr, daddr)
+ process = Popen(
+ ['open', '-a', 'Maps', maps_address],
+ stdout=PIPE, stderr=PIPE)
+ stdout, stderr = process.communicate()
+
+
+def instance():
+ '''
+ Instance for facade proxy.
+ '''
+ return MacOSMaps()

diff --git a/sbapp/plyer/platforms/macosx/notification.py b/sbapp/plyer/platforms/macosx/notification.py
index ed78ab11..35ce67ab 100644
--- a/sbapp/plyer/platforms/macosx/notification.py
+++ b/sbapp/plyer/platforms/macosx/notification.py
@@ -2,7 +2,7 @@
Module of MacOS API for plyer.notification.
'''
-from plyer.facades import Notification
+from sbapp.plyer.facades import Notification
from pyobjus import (
autoclass, protocol, objc_str, ObjcBOOL

diff --git a/sbapp/plyer/platforms/macosx/screenshot.py b/sbapp/plyer/platforms/macosx/screenshot.py
index c76766ad..16a35c47 100644
--- a/sbapp/plyer/platforms/macosx/screenshot.py
+++ b/sbapp/plyer/platforms/macosx/screenshot.py
@@ -1,8 +1,8 @@
import subprocess
from os.path import join
-from plyer.facades import Screenshot
-from plyer.utils import whereis_exe
-from plyer.platforms.macosx.storagepath import OSXStoragePath
+from sbapp.plyer.facades import Screenshot
+from sbapp.plyer.utils import whereis_exe
+from sbapp.plyer.platforms.macosx.storagepath import OSXStoragePath
class OSXScreenshot(Screenshot):

diff --git a/sbapp/plyer/platforms/macosx/sms.py b/sbapp/plyer/platforms/macosx/sms.py
new file mode 100644
index 00000000..86344436
--- /dev/null
+++ b/sbapp/plyer/platforms/macosx/sms.py
@@ -0,0 +1,42 @@
+from subprocess import Popen, PIPE
+from sbapp.plyer.facades import Sms as SMS
+from sbapp.plyer.utils import whereis_exe
+
+
+class MacOSSMS(SMS):
+ '''
+ Implementation of macOS' Messages API
+ '''
+
+ def _send(self, **kwargs):
+ '''
+ Will send `message` to `recipient` via Messages app
+
+ By default, if `mode` is not explicitly set, `iMessage` is used.
+ In order to use `SMS` mode, a valid carrier-activated device must
+ be connected and configured.
+ '''
+
+ recipient = kwargs.get('recipient')
+ message = kwargs.get('message')
+ mode = kwargs.get('mode') # Supported modes: iMessage (default), SMS
+ if not mode:
+ mode = 'iMessage'
+
+ APPLESCRIPT = f"""tell application "Messages"
+ set targetService to 1st account whose service type = {mode}
+ set targetBuddy to participant "{recipient}" of targetService
+ send "{message}" to targetBuddy
+end tell"""
+
+ osascript_process = Popen(
+ ['osascript', '-e', APPLESCRIPT], stdout=PIPE, stderr=PIPE)
+ stdout, stderr = osascript_process.communicate()
+
+
+def instance():
+ import sys
+ if whereis_exe('osascript'):
+ return MacOSSMS()
+ sys.stderr.write('osascript not found.')
+ return SMS()

diff --git a/sbapp/plyer/platforms/macosx/storagepath.py b/sbapp/plyer/platforms/macosx/storagepath.py
index 027b04ee..79973a47 100644
--- a/sbapp/plyer/platforms/macosx/storagepath.py
+++ b/sbapp/plyer/platforms/macosx/storagepath.py
@@ -3,7 +3,7 @@ MacOS X Storage Path
--------------------
'''
-from plyer.facades import StoragePath
+from sbapp.plyer.facades import StoragePath
from pyobjus import autoclass
NSFileManager = autoclass('NSFileManager')

diff --git a/sbapp/plyer/platforms/macosx/tts.py b/sbapp/plyer/platforms/macosx/tts.py
index 755e820d..464de560 100644
--- a/sbapp/plyer/platforms/macosx/tts.py
+++ b/sbapp/plyer/platforms/macosx/tts.py
@@ -1,6 +1,6 @@
import subprocess
-from plyer.facades import TTS
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import TTS
+from sbapp.plyer.utils import whereis_exe
class NativeSayTextToSpeech(TTS):

diff --git a/sbapp/plyer/platforms/macosx/uniqueid.py b/sbapp/plyer/platforms/macosx/uniqueid.py
index dc841535..464e7238 100644
--- a/sbapp/plyer/platforms/macosx/uniqueid.py
+++ b/sbapp/plyer/platforms/macosx/uniqueid.py
@@ -4,8 +4,8 @@ Module of MacOS API for plyer.uniqueid.
from os import environ
from subprocess import Popen, PIPE
-from plyer.facades import UniqueID
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import UniqueID
+from sbapp.plyer.utils import whereis_exe
class OSXUniqueID(UniqueID):

diff --git a/sbapp/plyer/platforms/macosx/wifi.py b/sbapp/plyer/platforms/macosx/wifi.py
index 32c02ab6..61da3859 100644
--- a/sbapp/plyer/platforms/macosx/wifi.py
+++ b/sbapp/plyer/platforms/macosx/wifi.py
@@ -1,7 +1,7 @@
from pyobjus import autoclass
from pyobjus.dylib_manager import load_framework, INCLUDE
-from plyer.facades import Wifi
+from sbapp.plyer.facades import Wifi
load_framework(INCLUDE.Foundation)
load_framework(INCLUDE.CoreWLAN)

diff --git a/sbapp/plyer/platforms/win/audio.py b/sbapp/plyer/platforms/win/audio.py
index c9d8f357..dfe9edf0 100644
--- a/sbapp/plyer/platforms/win/audio.py
+++ b/sbapp/plyer/platforms/win/audio.py
@@ -14,8 +14,8 @@ from ctypes import (
)
from ctypes.wintypes import DWORD, UINT
-from plyer.facades import Audio
-from plyer.platforms.win.storagepath import WinStoragePath
+from sbapp.plyer.facades import Audio
+from sbapp.plyer.platforms.win.storagepath import WinStoragePath
# DWORD_PTR i.e. ULONG_PTR, 32/64bit
ULONG_PTR = c_ulonglong if sizeof(c_void_p) == 8 else c_ulong

diff --git a/sbapp/plyer/platforms/win/battery.py b/sbapp/plyer/platforms/win/battery.py
index 861d9048..af899cac 100644
--- a/sbapp/plyer/platforms/win/battery.py
+++ b/sbapp/plyer/platforms/win/battery.py
@@ -2,8 +2,8 @@
Module of Windows API for plyer.battery.
'''
-from plyer.platforms.win.libs.batterystatus import battery_status
-from plyer.facades import Battery
+from sbapp.plyer.platforms.win.libs.batterystatus import battery_status
+from sbapp.plyer.facades import Battery
from ctypes.wintypes import BYTE

diff --git a/sbapp/plyer/platforms/win/cpu.py b/sbapp/plyer/platforms/win/cpu.py
index d1262dcb..9b655492 100644
--- a/sbapp/plyer/platforms/win/cpu.py
+++ b/sbapp/plyer/platforms/win/cpu.py
@@ -11,7 +11,7 @@ from ctypes.wintypes import (
BYTE, DWORD, WORD
)
-from plyer.facades import CPU
+from sbapp.plyer.facades import CPU
KERNEL = windll.kernel32

diff --git a/sbapp/plyer/platforms/win/devicename.py b/sbapp/plyer/platforms/win/devicename.py
index d35d76bd..d631ac4b 100644
--- a/sbapp/plyer/platforms/win/devicename.py
+++ b/sbapp/plyer/platforms/win/devicename.py
@@ -3,7 +3,7 @@ Module of Win API for plyer.devicename.
'''
import socket
-from plyer.facades import DeviceName
+from sbapp.plyer.facades import DeviceName
class WinDeviceName(DeviceName):

diff --git a/sbapp/plyer/platforms/win/email.py b/sbapp/plyer/platforms/win/email.py
index 4c0f9b76..3c43cdee 100644
--- a/sbapp/plyer/platforms/win/email.py
+++ b/sbapp/plyer/platforms/win/email.py
@@ -7,7 +7,7 @@ try:
from urllib.parse import quote
except ImportError:
from urllib import quote
-from plyer.facades import Email
+from sbapp.plyer.facades import Email
class WindowsEmail(Email):

diff --git a/sbapp/plyer/platforms/win/filechooser.py b/sbapp/plyer/platforms/win/filechooser.py
index d61fdc11..9932d9bf 100644
--- a/sbapp/plyer/platforms/win/filechooser.py
+++ b/sbapp/plyer/platforms/win/filechooser.py
@@ -3,7 +3,7 @@ Windows file chooser
--------------------
'''
-from plyer.facades import FileChooser
+from sbapp.plyer.facades import FileChooser
from win32com.shell.shell import (
SHBrowseForFolder as browse,
SHGetPathFromIDList as get_path
@@ -84,7 +84,7 @@ class Win32FileChooser:
# e.g. open_file(filters=['*.txt', '*.py'])
filters = ""
for f in self.filters:
- if type(f) == str:
+ if isinstance(f, str):
filters += (f + "\x00") * 2
else:
filters += f[0] + "\x00" + ";".join(f[1:]) + "\x00"

diff --git a/sbapp/plyer/platforms/win/keystore.py b/sbapp/plyer/platforms/win/keystore.py
index 0065a6ab..59e48d1f 100644
--- a/sbapp/plyer/platforms/win/keystore.py
+++ b/sbapp/plyer/platforms/win/keystore.py
@@ -3,7 +3,7 @@ try:
except Exception:
raise NotImplementedError()
-from plyer.facades import Keystore
+from sbapp.plyer.facades import Keystore
class WinKeystore(Keystore):

diff --git a/sbapp/plyer/platforms/win/libs/balloontip.py b/sbapp/plyer/platforms/win/libs/balloontip.py
index 5494f4e5..1334a99a 100644
--- a/sbapp/plyer/platforms/win/libs/balloontip.py
+++ b/sbapp/plyer/platforms/win/libs/balloontip.py
@@ -12,7 +12,7 @@ import ctypes
import atexit
from threading import RLock
-from plyer.platforms.win.libs import win_api_defs
+from sbapp.plyer.platforms.win.libs import win_api_defs
WS_OVERLAPPED = 0x00000000

diff --git a/sbapp/plyer/platforms/win/libs/batterystatus.py b/sbapp/plyer/platforms/win/libs/batterystatus.py
index 1d8202dc..0c0c6350 100644
--- a/sbapp/plyer/platforms/win/libs/batterystatus.py
+++ b/sbapp/plyer/platforms/win/libs/batterystatus.py
@@ -6,7 +6,7 @@ __all__ = ('battery_status')
import ctypes
-from plyer.platforms.win.libs import win_api_defs
+from sbapp.plyer.platforms.win.libs import win_api_defs
def battery_status():

diff --git a/sbapp/plyer/platforms/win/notification.py b/sbapp/plyer/platforms/win/notification.py
index bac381ba..b83838e1 100644
--- a/sbapp/plyer/platforms/win/notification.py
+++ b/sbapp/plyer/platforms/win/notification.py
@@ -4,8 +4,8 @@ Module of Windows API for plyer.notification.
from threading import Thread as thread
-from plyer.facades import Notification
-from plyer.platforms.win.libs.balloontip import balloon_tip
+from sbapp.plyer.facades import Notification
+from sbapp.plyer.platforms.win.libs.balloontip import balloon_tip
class WindowsNotification(Notification):

diff --git a/sbapp/plyer/platforms/win/screenshot.py b/sbapp/plyer/platforms/win/screenshot.py
index b04eab57..1291fe99 100644
--- a/sbapp/plyer/platforms/win/screenshot.py
+++ b/sbapp/plyer/platforms/win/screenshot.py
@@ -18,8 +18,8 @@ from win32con import (
SRCCOPY
)
-from plyer.facades import Screenshot
-from plyer.platforms.win.storagepath import WinStoragePath
+from sbapp.plyer.facades import Screenshot
+from sbapp.plyer.platforms.win.storagepath import WinStoragePath
class WinScreenshot(Screenshot):

diff --git a/sbapp/plyer/platforms/win/storagepath.py b/sbapp/plyer/platforms/win/storagepath.py
index fd905d8a..1528483b 100755
--- a/sbapp/plyer/platforms/win/storagepath.py
+++ b/sbapp/plyer/platforms/win/storagepath.py
@@ -3,9 +3,9 @@ Windows Storage Path
--------------------
'''
-from plyer.facades import StoragePath
+from sbapp.plyer.facades import StoragePath
from os.path import expanduser
-from plyer.platforms.win.libs.win_api_defs import get_PATH
+from sbapp.plyer.platforms.win.libs.win_api_defs import get_PATH
from uuid import UUID

diff --git a/sbapp/plyer/platforms/win/tts.py b/sbapp/plyer/platforms/win/tts.py
index e2539c3e..2b9f8f9f 100644
--- a/sbapp/plyer/platforms/win/tts.py
+++ b/sbapp/plyer/platforms/win/tts.py
@@ -1,6 +1,6 @@
import subprocess
-from plyer.facades import TTS
-from plyer.utils import whereis_exe
+from sbapp.plyer.facades import TTS
+from sbapp.plyer.utils import whereis_exe
class EspeakTextToSpeech(TTS):

diff --git a/sbapp/plyer/platforms/win/uniqueid.py b/sbapp/plyer/platforms/win/uniqueid.py
index 6d423911..42853b71 100644
--- a/sbapp/plyer/platforms/win/uniqueid.py
+++ b/sbapp/plyer/platforms/win/uniqueid.py
@@ -10,7 +10,7 @@ except ImportError:
except ImportError:
raise NotImplementedError()
-from plyer.facades import UniqueID
+from sbapp.plyer.facades import UniqueID
class WinUniqueID(UniqueID):

diff --git a/sbapp/plyer/platforms/win/wifi.py b/sbapp/plyer/platforms/win/wifi.py
index 68efb2ff..152d4c04 100644
--- a/sbapp/plyer/platforms/win/wifi.py
+++ b/sbapp/plyer/platforms/win/wifi.py
@@ -1,5 +1,5 @@
-import plyer.platforms.win.libs.wifi_defs as wifi_lib
-from plyer.facades import Wifi
+import sbapp.plyer.platforms.win.libs.wifi_defs as wifi_lib
+from sbapp.plyer.facades import Wifi
class WindowWifi(Wifi):

diff --git a/sbapp/plyer/tests/__init__.py b/sbapp/plyer/tests/__init__.py
deleted file mode 100644
index e69de29b..00000000

diff --git a/sbapp/plyer/tests/common.py b/sbapp/plyer/tests/common.py
deleted file mode 100644
index 7436db82..00000000
--- a/sbapp/plyer/tests/common.py
+++ /dev/null
@@ -1,76 +0,0 @@
-'''
-Common objects for testing
-==========================
-
-* :class:`PlatformTest` - used as a decorator, allows running a test function
- only on a specific platform (see `plyer.utils.platform`).
-* :func:`platform_import` - manual import of a platform specific class instead
- of using `plyer.facades.*` proxies.
-'''
-
-import traceback
-from os import sep
-from os.path import normpath, splitdrive
-from plyer.utils import platform as plyer_platform
-
-
-class PlatformTest:
- '''
- Class for the @PlatformTest decorator to prevent running tests
- calling platform dependent API on different platforms.
- '''
-
- def __init__(self, platform):
- self.platform = platform
-
- def __call__(self, func):
- platform = self.platform
-
- if platform != plyer_platform:
- print("Skipping test '{}' - not on '{}'".format(
- func.__name__, platform
- ))
- func = self.eat
- return func
-
- @staticmethod
- def eat(*args, **kwargs):
- '''
- Simply eat all positional and keyword arguments
- and return None as an empty function.
- '''
-
-
-def platform_import(platform, module_name, whereis_exe=None):
- '''
- Import platform API directly instead of through Proxy.
- '''
-
- try:
- module = 'plyer.platforms.{}.{}'.format(
- platform, module_name
- )
- mod = __import__(module, fromlist='.')
-
- except ImportError as exc:
- print(vars(exc))
- traceback.print_exc()
-
- if whereis_exe:
- mod.whereis_exe = whereis_exe
- return mod
-
-
-def splitpath(path):
- '''
- Split string path into a list of folders (+ file if available).
- '''
- if path[0] == sep and path[1] != sep:
- path = path[1:]
- path = normpath(path).split(sep)
- else:
- drive, path = splitdrive(path)
- if path[0] == sep and path[1] != sep:
- path = path[1:]
- path = [drive, ] + normpath(path).split(sep)
- return path

diff --git a/sbapp/plyer/tests/images/kivy32.ico b/sbapp/plyer/tests/images/kivy32.ico
deleted file mode 100644
index 2010bf3c..00000000
Binary files a/sbapp/plyer/tests/images/kivy32.ico and /dev/null differ

diff --git a/sbapp/plyer/tests/test_audio.py b/sbapp/plyer/tests/test_audio.py
deleted file mode 100644
index e8118913..00000000
--- a/sbapp/plyer/tests/test_audio.py
+++ /dev/null
@@ -1,110 +0,0 @@
-'''
-TestAudio
-=========
-
-Tested platforms:
-
-* macOS
-* Windows
-
-.. versionadded:: 1.4.0
-'''
-
-import unittest
-import time
-
-from os import mkdir, remove, environ
-from os.path import join, expanduser, exists
-from plyer.tests.common import platform_import, PlatformTest
-
-
-class TestAudio(unittest.TestCase):
- '''
- TestCase for plyer.audio.
-
- .. versionadded:: 1.4.0
- '''
-
- @PlatformTest('macosx')
- def test_audio_macosx(self):
- '''
- Test macOS audio start, stop and play
-
- .. versionadded:: 1.4.0
- '''
-
- path = join(expanduser('~'), 'Music')
- if not exists(path):
- mkdir(path)
-
- audio = platform_import(
- platform='macosx',
- module_name='audio',
- )
-
- self.assertIn('OSXAudio', dir(audio))
- audio = audio.instance()
- self.assertIn('OSXAudio', str(audio))
-
- self.assertFalse(exists(audio.file_path))
- self.assertIsNone(audio.start())
- time.sleep(0.5)
- self.assertIsNone(audio.stop())
- self.assertIsNone(audio.play())
- time.sleep(0.5)
- self.assertIsNone(audio.stop())
-
- audio.file_path = audio.file_path.replace(
- 'file://', ''
- )
-
- self.assertTrue(exists(audio.file_path))
-
- remove(audio.file_path)
-
- @PlatformTest('win')
- def test_audio_win(self):
- '''
- Test Windows audio start, stop and play
-
- .. versionadded:: 1.4.0
- '''
-
- if environ.get('APPVEYOR'):
- # Appveyor has no recording device installed
- # therefore the test will 100% fail
- #
- # error_code: 328
- # message:
- # 'No wave device is installed that can record files in the current
- # format. To install a wave device, go to Control Panel, click P')
- return
-
- path = join(environ['USERPROFILE'], 'Music')
- if not exists(path):
- mkdir(path)
-
- audio = platform_import(
- platform='win',
- module_name='audio',
- )
-
- self.assertIn('WinAudio', dir(audio))
- audio = audio.instance()
- self.assertIn('WinAudio', str(audio))
-
- self.assertFalse(exists(audio.file_path))
- self.assertIsNone(audio.start())
- time.sleep(0.5)
- self.assertIsNone(audio.stop())
- self.assertIsNone(audio.play())
- time.sleep(0.5)
- self.assertIsNone(audio.stop())
-
- self.assertTrue(exists(audio.file_path))
-
- remove(audio.file_path)
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_battery.py b/sbapp/plyer/tests/test_battery.py
deleted file mode 100644
index b44d504d..00000000
--- a/sbapp/plyer/tests/test_battery.py
+++ /dev/null
@@ -1,413 +0,0 @@
-'''
-TestBattery
-===========
-
-Tested platforms:
-
-* Windows
-* Linux - upower, kernel sysclass
-* macOS - ioreg
-'''
-
-import unittest
-from io import BytesIO
-from os.path import join
-from textwrap import dedent
-from mock import patch, Mock
-
-from plyer.tests.common import PlatformTest, platform_import
-
-
-class MockedKernelSysclass:
- '''
- Mocked object used instead of Linux's sysclass for power_supply
- battery uevent.
- '''
-
- @property
- def path(self):
- '''
- Mocked path to Linux kernel sysclass.
- '''
- return join('/sys', 'class', 'power_supply', 'BAT0')
-
- @property
- def charging(self):
- '''
- Mocked battery charging status.
- '''
- return u'Discharging'
-
- @property
- def percentage(self):
- '''
- Mocked battery charge percentage.
- '''
- return 89.0
-
- @property
- def full(self):
- '''
- Mocked full battery charge.
- '''
- return 4764000
-
- @property
- def now(self):
- '''
- Calculated current mocked battery charge.
- '''
- return self.percentage * self.full / 100.0
-
- @property
- def uevent(self):
- '''
- Mocked /sys/class/power_supply/BAT0 file.
- '''
- return BytesIO(dedent(b'''\
- POWER_SUPPLY_NAME=BAT0
- POWER_SUPPLY_STATUS={}
- POWER_SUPPLY_PRESENT=1
- POWER_SUPPLY_TECHNOLOGY=Li-ion
- POWER_SUPPLY_CYCLE_COUNT=0
- POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
- POWER_SUPPLY_VOLTAGE_NOW=12074000
- POWER_SUPPLY_CURRENT_NOW=1584000
- POWER_SUPPLY_CHARGE_FULL_DESIGN=5800000
- POWER_SUPPLY_CHARGE_FULL={}
- POWER_SUPPLY_CHARGE_NOW={}
- POWER_SUPPLY_CAPACITY={}
- POWER_SUPPLY_CAPACITY_LEVEL=Normal
- POWER_SUPPLY_MODEL_NAME=1005HA
- POWER_SUPPLY_MANUFACTURER=ASUS
- POWER_SUPPLY_SERIAL_NUMBER=0
- '''.decode('utf-8').format(
- self.charging, self.full,
- self.now, int(self.percentage)
- )).encode('utf-8'))
-
-
-class MockedUPower:
- '''
- Mocked object used instead of 'upower' binary in the Linux specific API
- plyer.platforms.linux.battery. The same output structure is tested for
- the range of <min_version, max_version>.
-
- .. note:: Extend the object with another data sample if it does not match.
- '''
-
- min_version = '0.99.4'
- max_version = '0.99.4'
-
- values = {
- u'Device': u'/org/freedesktop/UPower/devices/battery_BAT0',
- u'native-path': u'BAT0',
- u'vendor': u'ASUS',
- u'model': u'1005HA',
- u'power supply': u'yes',
- u'updated': u'Thu 05 Jul 2018 23:15:01 PM CEST',
- u'has history': u'yes',
- u'has statistics': u'yes',
- u'battery': {
- u'present': u'yes',
- u'rechargeable': u'yes',
- u'state': u'discharging',
- u'warning-level': u'none',
- u'energy': u'48,708 Wh',
- u'energy-empty': u'0 Wh',
- u'energy-full': u'54,216 Wh',
- u'energy-full-design': u'62,64 Wh',
- u'energy-rate': u'7,722 W',
- u'voltage': u'11,916 V',
- u'time to empty': u'6,3 hours',
- u'percentage': u'89%',
- u'capacity': u'86,5517%',
- u'technology': u'lithium-ion',
- u'icon-name': u"'battery-full-symbolic"
- },
- u'History (charge)': u'1530959637 89,000 discharging',
- u'History (rate)': u'1530958556 7,474 discharging'
- }
-
- data = str(
- ' native-path: {native-path}\n'
- ' vendor: {vendor}\n'
- ' model: {model}\n'
- ' power supply: {power supply}\n'
- ' updated: {updated}\n'
- ' has history: {has history}\n'
- ' has statistics: {has statistics}\n'
- ' battery\n'
- ' present: {battery[present]}\n'
- ' rechargeable: {battery[rechargeable]}\n'
- ' state: {battery[state]}\n'
- ' warning-level: {battery[warning-level]}\n'
- ' energy: {battery[energy]}\n'
- ' energy-empty: {battery[energy-empty]}\n'
- ' energy-full: {battery[energy-full]}\n'
- ' energy-full-design: {battery[energy-full-design]}\n'
- ' energy-rate: {battery[energy-rate]}\n'
- ' voltage: {battery[voltage]}\n'
- ' time to empty: {battery[time to empty]}\n'
- ' percentage: {battery[percentage]}\n'
- ' capacity: {battery[capacity]}\n'
- ' technology: {battery[technology]}\n'
- ' icon-name: {battery[icon-name]}\n'
- ' History (charge):\n'
- ' {History (charge)}\n'
- ' History (rate):\n'
- ' {History (rate)}\n'
- ).format(**values).encode('utf-8')
- # LinuxBattery calls decode()
-
- def __init__(self, *args, **kwargs):
- # only to ignore all args, kwargs
- pass
-
- @staticmethod
- def communicate():
- '''
- Mock Popen.communicate, so that 'upower' isn't used.
- '''
- return (MockedUPower.data, )
-
- @staticmethod
- def whereis_exe(binary):
- '''
- Mock whereis_exe, so that it looks like
- Linux UPower binary is present on the system.
- '''
- return binary == 'upower'
-
- @staticmethod
- def charging():
- '''
- Return charging bool from mocked data.
- '''
- return MockedUPower.values['battery']['state'] == 'charging'
-
- @staticmethod
- def percentage():
- '''
- Return percentage from mocked data.
- '''
- percentage = MockedUPower.values['battery']['percentage'][:-1]
- return float(percentage.replace(',', '.'))
-
-
-class MockedIOReg:
- '''
- Mocked object used instead of Apple's ioreg.
- '''
- values = {
- "MaxCapacity": "5023",
- "CurrentCapacity": "4222",
- "IsCharging": "No"
- }
-
- output = dedent(
- """+-o AppleSmartBattery <class AppleSmartBattery,\
- id 0x1000002c9, registered, matched, active, busy 0 (0 ms), retain 6>
- {{
- "TimeRemaining" = 585
- "AvgTimeToEmpty" = 585
- "InstantTimeToEmpty" = 761
- "ExternalChargeCapable" = Yes
- "FullPathUpdated" = 1541845134
- "CellVoltage" = (4109,4118,4099,0)
- "PermanentFailureStatus" = 0
- "BatteryInvalidWakeSeconds" = 30
- "AdapterInfo" = 0
- "MaxCapacity" = {MaxCapacity}
- "Voltage" = 12326
- "DesignCycleCount70" = 13
- "Manufacturer" = "SWD"
- "Location" = 0
- "CurrentCapacity" = {CurrentCapacity}
- "LegacyBatteryInfo" = {{"Amperage"=18446744073709551183,"Flags"=4,\
- "Capacity"=5023,"Current"=4222,"Voltage"=12326,"Cycle Count"=40}}
- "FirmwareSerialNumber" = 1
- "BatteryInstalled" = Yes
- "PackReserve" = 117
- "CycleCount" = 40
- "DesignCapacity" = 5088
- "OperationStatus" = 58435
- "ManufactureDate" = 19700
- "AvgTimeToFull" = 65535
- "BatterySerialNumber" = "1234567890ABCDEFGH"
- "BootPathUpdated" = 1541839734
- "PostDischargeWaitSeconds" = 120
- "Temperature" = 3038
- "UserVisiblePathUpdated" = 1541845194
- "InstantAmperage" = 18446744073709551249
- "ManufacturerData" = <000000000>
- "FullyCharged" = No
- "MaxErr" = 1
- "DeviceName" = "bq20z451"
- "IOGeneralInterest" = "IOCommand is not serializable"
- "Amperage" = 18446744073709551183
- "IsCharging" = {IsCharging}
- "DesignCycleCount9C" = 1000
- "PostChargeWaitSeconds" = 120
- "ExternalConnected" = No
- }}"""
- ).format(**values).encode('utf-8')
-
- def __init__(self, *args, **kwargs):
- # only to ignore all args, kwargs
- pass
-
- @staticmethod
- def communicate():
- '''
- Mock Popen.communicate, so that 'ioreg' isn't used.
- '''
- return (MockedIOReg.output, )
-
- @staticmethod
- def whereis_exe(binary):
- '''
- Mock whereis_exe, so that it looks like
- macOS ioreg binary is present on the system.
- '''
- return binary == 'ioreg'
-
- @staticmethod
- def charging():
- '''
- Return charging bool from mocked data.
- '''
- return MockedIOReg.values['IsCharging'] == 'Yes'
-
- @staticmethod
- def percentage():
- '''
- Return percentage from mocked data.
- '''
- current_capacity = int(MockedIOReg.values['CurrentCapacity'])
- max_capacity = int(MockedIOReg.values['MaxCapacity'])
- percentage = 100.0 * current_capacity / max_capacity
-
- return percentage
-
-
-class TestBattery(unittest.TestCase):
- '''
- TestCase for plyer.battery.
- '''
-
- def test_battery_linux_upower(self):
- '''
- Test mocked Linux UPower for plyer.battery.
- '''
- battery = platform_import(
- platform='linux',
- module_name='battery',
- whereis_exe=MockedUPower.whereis_exe
- )
- battery.Popen = MockedUPower
- battery = battery.instance()
-
- self.assertEqual(
- battery.status, {
- 'isCharging': MockedUPower.charging(),
- 'percentage': MockedUPower.percentage()
- }
- )
-
- def test_battery_linux_kernel(self):
- '''
- Test mocked Linux kernel sysclass for plyer.battery.
- '''
-
- def false(*args, **kwargs):
- return False
-
- sysclass = MockedKernelSysclass()
-
- with patch(target='os.path.exists') as bat_path:
- # first call to trigger exists() call
- platform_import(
- platform='linux',
- module_name='battery',
- whereis_exe=false
- ).instance()
- bat_path.assert_called_once_with(sysclass.path)
-
- # exists() checked with sysclass path
- # set mock to proceed with this branch
- bat_path.return_value = True
-
- battery = platform_import(
- platform='linux',
- module_name='battery',
- whereis_exe=false
- ).instance()
-
- stub = Mock(return_value=sysclass.uevent)
- target = 'builtins.open'
-
- with patch(target=target, new=stub):
- self.assertEqual(
- battery.status, {
- 'isCharging': sysclass.charging == 'Charging',
- 'percentage': sysclass.percentage
- }
- )
-
- @PlatformTest('win')
- def test_battery_win(self):
- '''
- Test Windows API for plyer.battery.
- '''
- battery = platform_import(
- platform='win',
- module_name='battery'
- ).instance()
- for key in ('isCharging', 'percentage'):
- self.assertIn(key, battery.status)
- self.assertIsNotNone(battery.status[key])
-
- def test_battery_macosx(self):
- '''
- Test macOS IOReg for plyer.battery.
- '''
- battery = platform_import(
- platform='macosx',
- module_name='battery',
- whereis_exe=MockedIOReg.whereis_exe
- )
-
- battery.Popen = MockedIOReg
- self.assertIn('OSXBattery', dir(battery))
- battery = battery.instance()
- self.assertIn('OSXBattery', str(battery))
-
- self.assertEqual(
- battery.status, {
- 'isCharging': MockedIOReg.charging(),
- 'percentage': MockedIOReg.percentage()
- }
- )
-
- def test_battery_macosx_instance(self):
- '''
- Test macOS instance for plyer.battery
- '''
-
- def no_exe(*args, **kwargs):
- return
-
- battery = platform_import(
- platform='macosx',
- module_name='battery',
- whereis_exe=no_exe
- )
-
- battery = battery.instance()
- self.assertNotIn('OSXBattery', str(battery))
- self.assertIn('Battery', str(battery))
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_bluetooth.py b/sbapp/plyer/tests/test_bluetooth.py
deleted file mode 100644
index 8997c4d3..00000000
--- a/sbapp/plyer/tests/test_bluetooth.py
+++ /dev/null
@@ -1,144 +0,0 @@
-'''
-TestBluetooth
-=============
-
-Tested platforms:
-
-* macOS - system_profiler
-'''
-
-import unittest
-
-from plyer.tests.common import platform_import
-from textwrap import dedent
-
-
-class MockedSystemProfiler:
- '''
- Mocked object used instead of Apple's system_profiler
- '''
- value = "On"
- output = dedent(
- """Bluetooth:
-
- Apple Bluetooth Software Version: 6.0.7f11
- Hardware, Features, and Settings:
- Address: AA-00-BB-11-CC-22
- Bluetooth Low Energy Supported: Yes
- Handoff Supported: Yes
- Instant Hot Spot Supported: Yes
- Manufacturer: Broadcom
- Transport: UART
- Chipset: 1234
- Firmware Version: v00 c0000
- Bluetooth Power: {}
- Auto Seek Pointing: On
- Remote wake: On
- Vendor ID: 0x0000
- Product ID: 0x0000
- HCI Version: (0x0)
- HCI Revision: 0x0000
- LMP Version: (0x0)
- LMP Subversion: 0x0000
- Auto Seek Keyboard: On
- Devices (Paired, Configured, etc.):
- iPhone:
- Address: AA-00-BB-11-CC-22
- Major Type: Miscellaneous
- Minor Type: Unknown
- Services:
- Paired: No
- Configured: Yes
- Connected: No
- Class of Device: 0x00 0x00 0x0000
- Services:
- Bluetooth File Transfer:
- Folder other devices can browse: ~/Public
- When receiving items: Accept all without warning
- State: Disabled
- Bluetooth File Exchange:
- Folder for accepted items: ~/Downloads
- When other items are accepted: Save to location
- When receiving items: Accept all without warning
- State: Disabled
- Bluetooth Internet Sharing:
- State: Disabled
- Incoming Serial Ports:
- Bluetooth-Incoming-Port:
- RFCOMM Channel: 3
- Requires Authentication: No"""
- ).format(value).encode('utf-8')
-
- def __init__(self, *args, **kwargs):
- # only to ignore all args, kwargs
- pass
-
- @staticmethod
- def communicate():
- '''
- Mock Popen.communicate, so that 'system_profiler'
- isn't used.
- '''
- return (MockedSystemProfiler.output, )
-
- @staticmethod
- def whereis_exe(binary):
- '''
- Mock whereis_exe, so that it looks like
- macOS system_profiler binary is present on the system.
- '''
- return binary == 'system_profiler'
-
- @staticmethod
- def get_info():
- '''
- Return current bluetooth status from mocked output.
- '''
- return MockedSystemProfiler.value
-
-
-class TestBluetooth(unittest.TestCase):
- '''
- TestCase for plyer.bluetooth.
- '''
-
- def test_bluetooth_macosx(self):
- '''
- Test macOS system_profiler for plyer.bluetooth.
- '''
- bluetooth = platform_import(
- platform='macosx',
- module_name='bluetooth',
- whereis_exe=MockedSystemProfiler.whereis_exe
- )
-
- bluetooth.Popen = MockedSystemProfiler
- self.assertIn('OSXBluetooth', dir(bluetooth))
- bluetooth = bluetooth.instance()
- self.assertIn('OSXBluetooth', str(bluetooth))
-
- self.assertEqual(
- bluetooth.info, MockedSystemProfiler.get_info()
- )
-
- def test_bluetooth_macosx_instance(self):
- '''
- Test macOS instance for plyer.bluetooth.
- '''
-
- def no_exe(*args, **kwargs):
- return
-
- bluetooth = platform_import(
- platform='macosx',
- module_name='bluetooth',
- whereis_exe=no_exe
- )
-
- bluetooth = bluetooth.instance()
- self.assertNotIn('OSXBluetooth', str(bluetooth))
- self.assertIn('Bluetooth', str(bluetooth))
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_cpu.py b/sbapp/plyer/tests/test_cpu.py
deleted file mode 100644
index 8882522c..00000000
--- a/sbapp/plyer/tests/test_cpu.py
+++ /dev/null
@@ -1,262 +0,0 @@
-'''
-TestCPU
-=======
-
-Tested platforms:
-
-* Windows
-* Linux - nproc
-'''
-
-import unittest
-from os import environ
-from os.path import join
-from mock import patch, Mock
-from textwrap import dedent
-
-from plyer.tests.common import PlatformTest, platform_import, splitpath
-
-
-class MockedKernelCPU:
- def __init__(self, *args, **kwargs):
- self.fname = args[0] if args else ''
- self.cpu_path = join('/sys', 'devices', 'system', 'cpu')
- self.cores = 16
- self.indicies = 4
-
- def __enter__(self, *args):
- file_value = None
- cpu_path = self.cpu_path
- spath = splitpath(self.fname)
-
- if self.fname == join(cpu_path, 'present'):
- file_value = Mock()
- file_value.read.return_value = self.present
- elif spath[5] == 'cache' and spath[7] == 'level':
- file_value = Mock()
- # force bytes, because reading files as bytes
- file_value.read.return_value = str(
- self.index_types[spath[4]][spath[6]][spath[7]]
- ).encode('utf-8')
- return file_value
-
- def __exit__(self, *args):
- pass
-
- @property
- def present(self):
- rng = list(range(self.cores))
- start = str(rng[0])
- end = str(rng[-1])
- if start == end: # cores == 1 --> b'0'
- value = str(start)
- else: # cores > 1 --> b'0-n'
- value = str('-'.join([start, end]))
- return value.encode('utf-8')
-
- @property
- def listdir(self):
- return ['index{}'.format(i) for i in range(self.indicies)]
-
- @property
- def index_types(self):
- # assign L1 to index0-1, L2 to 2, L3 to 3
- types = {0: 1, 1: 1, 2: 2, 3: 3}
-
- return {
- 'cpu{}'.format(c): {
- 'index{}'.format(i): {
- 'level': types[i]
- }
- for i in range(self.indicies)
- }
- for c in range(self.cores)
- }
-
-
-class MockedNProc:
- '''
- Mocked object used instead of 'nproc' binary in the Linux specific API
- plyer.platforms.linux.cpu. The same output structure is tested for
- the range of <min_version, max_version>.
-
- .. note:: Extend the object with another data sample if it does not match.
- '''
-
- min_version = '8.21'
- max_version = '8.21'
- logical_cores = 99
-
- def __init__(self, *args, **kwargs):
- # only to ignore all args, kwargs
- pass
-
- @staticmethod
- def communicate():
- '''
- Mock Popen.communicate, so that 'nproc' isn't used.
- '''
- return (str(MockedNProc.logical_cores).encode('utf-8'), )
-
- @staticmethod
- def whereis_exe(binary):
- '''
- Mock whereis_exe, so that it looks like
- Linux NProc binary is present on the system.
- '''
- return binary == 'nproc'
-
- @staticmethod
- def logical():
- '''
- Return percentage from mocked data.
- '''
- return int(MockedNProc.logical_cores)
-
-
-class MockedProcinfo:
- # docs:
- # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
- # /tree/arch/x86/kernel/cpu/proc.c
- sockets = 1 # physical id
- physical = 2 # core id
- threads_per_core = 2 # Intel specs document for i7-4500U
- logical = physical * threads_per_core # processor
-
- def __init__(self, *args, **kwargs):
- self.fname = args[0] if args else ''
-
- self.output = []
- __step = 0 # 0,1,0,1 -> 0,0,1,1
- for soc in range(self.sockets):
- for log in range(self.logical):
- if log != 0 and not log % self.physical:
- __step += 1
- self.output.append((dedent(
- '''\
- processor\t: {logical}
- vendor_id\t: GenuineIntel
- cpu family\t: 6
- model\t\t: 69
- model name\t: Intel(R) Core(TM) i7-4500U CPU @ 1.80GHz
- stepping\t: 1
- microcode\t: 0x17
- cpu MHz\t\t: 774.000
- cache size\t: 4096 KB
- physical id\t: {socket}
- siblings\t: 4
- core id\t\t: {physical}
- cpu cores\t: {threads_per_core}
- apicid\t\t: {logical}
- initial apicid\t: 0
- fpu\t\t: yes
- fpu_exception\t: yes
- cpuid level\t: 13
- wp\t\t: yes
- flags\t\t: fpu vme de pse tsc msr pae mce cx8 ...
- bogomips\t: 3591.40
- clflush size\t: 64
- cache_alignment\t: 64
- address sizes\t: 39 bits physical, 48 bits virtual
- power management:
- \n'''
- )).format(**{
- 'socket': soc,
- 'physical': __step,
- 'logical': log,
- 'threads_per_core': self.threads_per_core
- }))
- self.output = ''.join(self.output).encode('utf-8')
-
- def __enter__(self, *args):
- file_value = None
-
- if self.fname == '/proc/cpuinfo':
- file_value = Mock()
- file_value.readlines.return_value = self.output.split(
- '\n'.encode('utf-8')
- )
- return file_value
-
- def __exit__(self, *args):
- pass
-
-
-class TestCPU(unittest.TestCase):
- '''
- TestCase for plyer.cpu.
- '''
-
- def test_cpu_linux_physical(self):
- cpu = platform_import(
- platform='linux',
- module_name='cpu',
- whereis_exe=lambda b: b == 'nproc'
- ).instance()
-
- stub = MockedProcinfo
- target = 'builtins.open'
-
- with patch(target=target, new=stub):
- sb = stub()
- self.assertEqual(
- cpu.physical, sb.physical
- )
-
- def test_cpu_linux_logical(self):
- '''
- Test mocked Linux NProc for plyer.cpu.
- '''
- cpu = platform_import(
- platform='linux',
- module_name='cpu',
- whereis_exe=MockedNProc.whereis_exe
- )
- cpu.Popen = MockedNProc
- cpu = cpu.instance()
-
- self.assertEqual(
- cpu.logical, MockedNProc.logical()
- )
-
- @PlatformTest('linux')
- def test_cpu_linux_cache(self):
- cpu = platform_import(
- platform='linux',
- module_name='cpu',
- whereis_exe=lambda b: b == 'nproc'
- ).instance()
-
- stub = MockedKernelCPU
- target = 'builtins.open'
- sub_target = 'plyer.platforms.linux.cpu.listdir'
-
- with patch(target=target, new=stub):
- with patch(target=sub_target, return_value=stub().listdir):
- sb = stub()
- self.assertEqual(
- cpu.cache, {
- 'L1': sb.cores * 2,
- 'L2': sb.cores,
- 'L3': sb.cores
- }
- )
-
- @PlatformTest('win')
- def test_cpu_win_logical(self):
- cpu = platform_import(
- platform='win',
- module_name='cpu'
- )
-
- cpu = cpu.instance()
- self.assertEqual(
- cpu.logical,
- # https://docs.microsoft.com/en-us/previous-versions/
- # windows/it-pro/windows-xp/bb490954(v=technet.10)
- int(environ['NUMBER_OF_PROCESSORS'])
- )
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_devicename.py b/sbapp/plyer/tests/test_devicename.py
deleted file mode 100644
index cbb0fc4b..00000000
--- a/sbapp/plyer/tests/test_devicename.py
+++ /dev/null
@@ -1,77 +0,0 @@
-'''
-TestDeviceName
-============
-
-Tested platforms:
-
-* Windows
-'''
-
-import unittest
-from mock import patch
-from plyer.tests.common import PlatformTest, platform_import
-import socket
-
-
-class TestDeviceName(unittest.TestCase):
- '''
- TestCase for plyer.devicename.
- '''
-
- @PlatformTest('win')
- def test_devicename_win(self):
- '''
- Test Windows API for plyer.devicename.
- '''
- devicename = platform_import(platform='win',
- module_name='devicename'
- )
- devicename_instance = devicename.instance()
-
- with patch.object(socket,
- 'gethostname',
- return_value='mocked_windows_hostname'
- ) as _:
-
- evaluated_device_name = devicename_instance.device_name
- self.assertEqual(evaluated_device_name, 'mocked_windows_hostname')
-
- @PlatformTest('linux')
- def test_devicename_linux(self):
- '''
- Test Linux API for plyer.devicename.
- '''
- devicename = platform_import(platform='linux',
- module_name='devicename'
- )
- devicename_instance = devicename.instance()
-
- with patch.object(socket,
- 'gethostname',
- return_value='mocked_linux_hostname'
- ) as _:
-
- evaluated_device_name = devicename_instance.device_name
- self.assertEqual(evaluated_device_name, 'mocked_linux_hostname')
-
- @PlatformTest('macosx')
- def test_devicename_macosx(self):
- '''
- Test MacOSX API for plyer.devicename.
- '''
- devicename = platform_import(platform='macosx',
- module_name='devicename'
- )
- devicename_instance = devicename.instance()
-
- with patch.object(socket,
- 'gethostname',
- return_value='mocked_macosx_hostname'
- ) as _:
-
- evaluated_device_name = devicename_instance.device_name
- self.assertEqual(evaluated_device_name, 'mocked_macosx_hostname')
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_email.py b/sbapp/plyer/tests/test_email.py
deleted file mode 100644
index 60aa86f0..00000000
--- a/sbapp/plyer/tests/test_email.py
+++ /dev/null
@@ -1,48 +0,0 @@
-'''
-TestEmail
-=========
-
-Tested platforms:
-
-* Windows
-'''
-
-import unittest
-
-from mock import Mock, patch
-from plyer.tests.common import PlatformTest, platform_import
-
-
-class TestEmail(unittest.TestCase):
- '''
- TestCase for plyer.email.
- '''
-
- @staticmethod
- @PlatformTest('win')
- def test_email_win():
- '''
- Test starting Windows email client for plyer.email.
- '''
- email = platform_import(
- platform='win',
- module_name='email'
- )
-
- try:
- test_mailto = 'mailto:recipient?subject=subject&body=text'
- with patch(target='os.startfile', new=Mock()) as startfile:
- email.instance().send(
- recipient='recipient',
- subject='subject',
- text='text'
- )
- startfile.assert_called_once_with(test_mailto)
- except WindowsError:
- # if WE is raised, email client isn't found,
- # but the platform code works correctly
- print('Mail client not found!')
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_facade.py b/sbapp/plyer/tests/test_facade.py
deleted file mode 100644
index b49b15f7..00000000
--- a/sbapp/plyer/tests/test_facade.py
+++ /dev/null
@@ -1,183 +0,0 @@
-'''
-TestFacade
-==========
-
-Tested platforms:
-
-* Android
-* iOS
-* Windows
-* MacOS
-* Linux
-'''
-
-import unittest
-
-import sys
-from types import MethodType
-
-from mock import Mock, patch
-
-import plyer
-
-
-def mock_platform_module(mod, platform, cls):
- '''
- Create a stub module for a specific platform. This module contains:
-
- * class inheriting from facade implementing the desired feature
- * 'instance' function returning an instance of the implementing class
- '''
-
- # assemble an instance returned from the instance() function
- # which is created from a dynamically created class
- # <class '<mod>.<platform><cls>'> e.g.:
- # <class 'plyer.platforms.win.dummy . WinDummy'>
- stub_inst = Mock(
- __module__=mod,
- __class__=type(
- '{}{}'.format(platform.title(), cls), (object, ), {
- '__module__': mod
- }
- ),
- )
-
- # manual 'return_value' assign to Mock, so that the instance() call
- # can return stub_inst's own instance instead of creating another
- # unnecessary Mock object
- stub_inst.return_value = stub_inst
-
- # bind custom function returning the class name to stub_inst instance,
- # so that instance().show() call requires 'self' i.e. instance parameter
- # for the function to access the instance's class name
- stub_inst.show = MethodType(lambda slf: slf, stub_inst)
-
- stub_mod = Mock(instance=stub_inst)
- return stub_mod
-
-
-# dummy pyjnius class to silence the import + config
-class DummyJnius:
- '''
- Mocked PyJNIus module.
- '''
-
- def __init__(self, *args, **kwargs):
- class JavaClass:
- '''
- Mocked PyJNIus JavaClass object.
- '''
-
- def __init__(self):
- self.ANDROID_VERSION = None
- self.SDK_INT = 1
- self.mActivity = None
-
- self.autoclass = lambda *a, **kw: JavaClass()
-
-
-class TestFacade(unittest.TestCase):
- '''
- TestCase for plyer.utils.Proxy and plyer.facades.
- '''
-
- def test_facade_existing_platforms(self):
- '''
- Test for returning an object for Android API implementation
- from Proxy object using a dynamically generated dummy objects.
- '''
- _original = plyer.utils.platform
-
- for plat in {'android', 'ios', 'win', 'macosx', 'linux'}:
- plyer.utils.platform = plat
-
- if plat == 'android':
- # android platform automatically imports jnius
- sys.modules['jnius'] = DummyJnius()
-
- # create stub module with instance func and class
- stub_mod = mock_platform_module(
- mod='plyer.platforms.{}.dummy'.format(plat),
- platform=plyer.utils.platform,
- cls='Dummy'
- )
-
- proxy_cls = plyer.utils.Proxy
- target = 'builtins.__import__'
-
- with patch(target=target, return_value=stub_mod):
- dummy = proxy_cls('dummy', stub_mod)
-
- self.assertEqual(
- str(dummy.__class__).split("'")[1],
- 'plyer.platforms.{}.dummy.{}Dummy'.format(
- plat, plat.title()
- )
- )
- self.assertEqual(
- str(dummy.show().__class__).split("'")[1],
- 'plyer.platforms.{}.dummy.{}Dummy'.format(
- plat, plat.title()
- )
- )
-
- plyer.utils.platform = _original
-
- def test_facade_unknown(self):
- '''
- Test fallback of Proxy to facade if there
- is no such requested platform.
- '''
-
- _original = plyer.utils.platform
- plyer.utils.platform = 'unknown'
-
- # no 'unknown' platform (folder), fallback to facade
- class MockedProxy(plyer.utils.Proxy):
- '''
- Partially mocked Proxy class, so that we pull the error
- from traceback.print_exc to the test and check the calls.
- '''
-
- # _ensure_obj is called only once, to either
- # get the platform object or fall back to facade
- # therefore the three self.asserts below will return
- # different values
- expected_asserts = [True, False, False]
-
- def _ensure_obj(inst):
- # called once, prints to stderr
-
- # mock stderr because traceback.print_exc uses it
- # https://github.com/python/cpython/blob/
- # 16dfca4d829e45f36e71bf43f83226659ce49315/Lib/traceback.py#L99
- sys.stderr = Mock()
-
- # call the original function to trigger
- # ImportError warnings in stderr
- super(MockedProxy, inst)._ensure_obj()
-
- # Traceback (most recent call last):
- # File "/app/plyer/utils.py", line 88, in _ensure_obj
- # mod = __import__(module, fromlist='.')
- # ImportError: No module named unknown.dummy
-
- # must not use self.assertX
- # (has to be checked on the go!)
- expected_bool = MockedProxy.expected_asserts.pop(0)
- call_count = sys.stderr.write.call_count
- assert (call_count == 6) == expected_bool, call_count
-
- # return stderr to the original state
- sys.stderr = sys.__stderr__
-
- proxy_cls = MockedProxy
- facade = Mock()
- dummy = proxy_cls('dummy', facade)
-
- self.assertEqual(dummy._mock_new_parent, facade)
- plyer.utils.platform = _original
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_notification.py b/sbapp/plyer/tests/test_notification.py
deleted file mode 100644
index 7177ad7a..00000000
--- a/sbapp/plyer/tests/test_notification.py
+++ /dev/null
@@ -1,209 +0,0 @@
-'''
-TestNotification
-================
-
-Tested platforms:
-
-* Windows
-* Linux - notify-send, dbus
-'''
-
-import unittest
-import sys
-
-from time import sleep
-from os.path import dirname, abspath, join
-
-from mock import Mock, patch
-from plyer.tests.common import PlatformTest, platform_import
-
-
-class MockedNotifySend:
- '''
- Mocked object used instead of the console-like calling
- of notify-send binary with parameters.
- '''
- @staticmethod
- def whereis_exe(binary):
- '''
- Mock whereis_exe, so that it looks like
- Linux notify-send binary is present on the system.
- '''
- return binary == 'notify-send'
-
- @staticmethod
- def call(args):
- '''
- Mocked subprocess.call to check console parameters.
- '''
- assert len(args) >= 3
- assert TestNotification.data['title'] in args
- assert TestNotification.data['message'] in args
-
- @staticmethod
- def warn(msg):
- '''
- Mocked warnings.warn, so that we check the custom ImportError message.
- '''
- assert 'dbus package is not installed' in msg
-
-
-class TestNotification(unittest.TestCase):
- '''
- TestCase for plyer.notification.
- '''
-
- data = {
- 'title': 'title',
- 'message': 'My Message\nis multiline',
- 'app_name': 'Plyer Test',
- 'app_icon': join(
- dirname(abspath(__file__)),
- 'images', 'kivy32.ico'
- ),
- 'timeout': 0.7
- }
-
- def show_notification(self, instance):
- '''
- Call notify() from platform specific instance with sample data.
- '''
- instance.notify(**self.data)
-
- @PlatformTest('win')
- def test_notification_windows(self):
- '''
- Test Windows API for plyer.notification.
- '''
- import ctypes
- from ctypes import (
- WINFUNCTYPE, POINTER,
- create_unicode_buffer,
- c_bool, c_int
- )
- notif = platform_import(
- platform='win',
- module_name='notification'
- ).instance()
- enum_windows = ctypes.windll.user32.EnumWindows
- get_class_name = ctypes.windll.user32.GetClassNameW
-
- # loop over windows and get refs to
- # the opened plyer notifications
- clsnames = []
-
- def fetch_class(hwnd, *args):
- '''
- EnumWindowsProc callback for EnumWindows.
- '''
- buff = create_unicode_buffer(50)
- get_class_name(hwnd, buff, 50)
-
- if 'Plyer' in buff.value:
- clsnames.append(buff.value)
-
- # ensure it's not an empty facade
- self.assertIn('WindowsNotification', str(notif))
-
- # create enum function for EnumWindows
- enum_windows_proc = WINFUNCTYPE(
- # returns
- c_bool,
-
- # input params: hwnd, lParam
- POINTER(c_int), POINTER(c_int)
- )
-
- for i in range(3):
- self.show_notification(notif)
-
- # the balloon needs some time to became visible in WinAPI
- sleep(0.2)
-
- # fetch window class names
- enum_windows(
- # enum & params
- enum_windows_proc(fetch_class), None
- )
-
- # 3 active balloons at the same time,
- # class_name is incremented - see WindowsBalloonTip
- self.assertEqual(len(clsnames), i + 1)
- self.assertIn('PlyerTaskbar' + str(i), clsnames)
- clsnames = []
-
- @PlatformTest('linux')
- def test_notification_dbus(self):
- '''
- Test mocked Linux DBus for plyer.notification.
- '''
- notif = platform_import(
- platform='linux',
- module_name='notification'
- )
- self.assertIn('NotifyDbus', dir(notif))
-
- # (3) mocked Interface called from dbus
- interface = Mock()
- interface.side_effect = (interface, )
-
- # (2) mocked SessionBus called from dbus
- session_bus = Mock()
- session_bus.side_effect = (session_bus, )
-
- # (1) mocked dbus for import
- dbus = Mock(SessionBus=session_bus, Interface=interface)
-
- # inject the mocked module
- self.assertNotIn('dbus', sys.modules)
- sys.modules['dbus'] = dbus
-
- try:
- notif = notif.instance()
- self.assertIn('NotifyDbus', str(notif))
-
- # call notify()
- self.show_notification(notif)
-
- # check whether Mocks were called
- dbus.SessionBus.assert_called_once()
-
- session_bus.get_object.assert_called_once_with(
- 'org.freedesktop.Notifications',
- '/org/freedesktop/Notifications'
- )
-
- interface.Notify.assert_called_once_with(
- TestNotification.data['app_name'],
- 0,
- TestNotification.data['app_icon'],
- TestNotification.data['title'],
- TestNotification.data['message'],
- [], {},
- TestNotification.data['timeout'] * 1000
- )
- finally:
- del sys.modules['dbus']
- self.assertNotIn('dbus', sys.modules)
-
- @PlatformTest('linux')
- def test_notification_notifysend(self):
- '''
- Test mocked Linux notify-send for plyer.notification.
- '''
- notif = platform_import(
- platform='linux',
- module_name='notification',
- whereis_exe=MockedNotifySend.whereis_exe
- )
- self.assertIn('NotifySendNotification', dir(notif))
- with patch(target='warnings.warn', new=MockedNotifySend.warn):
- notif = notif.instance()
- self.assertIn('NotifySendNotification', str(notif))
-
- with patch(target='subprocess.call', new=MockedNotifySend.call):
- self.assertIsNone(self.show_notification(notif))
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_screenshot.py b/sbapp/plyer/tests/test_screenshot.py
deleted file mode 100644
index 931fac28..00000000
--- a/sbapp/plyer/tests/test_screenshot.py
+++ /dev/null
@@ -1,137 +0,0 @@
-'''
-TestScreenshot
-==============
-
-Tested platforms:
-
-* MacOS
-* Linux
-'''
-
-import unittest
-
-from os import mkdir, remove
-from os.path import join, expanduser, exists
-
-from mock import patch
-from plyer.tests.common import PlatformTest, platform_import
-
-
-class MockedScreenCapture:
- '''
- Mocked object used instead of the console-like calling
- of screencapture binary with parameters.
- '''
- @staticmethod
- def whereis_exe(binary):
- '''
- Mock whereis_exe, so that it looks like
- MacOS screencapture binary is present on the system.
- '''
- return binary == 'screencapture'
-
- @staticmethod
- def call(args):
- '''
- Mocked subprocess.call to check console parameters.
- '''
- assert len(args) == 2, len(args)
- assert args[0] == 'screencapture', args
- assert args[1] == join(
- expanduser('~'), 'Pictures', 'screenshot.png'
- ), args
- with open(args[1], 'w') as scr:
- scr.write('')
-
-
-class MockedXWD:
- '''
- Mocked object used instead of the console-like calling
- of X11 xwd binary with parameters.
- '''
- @staticmethod
- def whereis_exe(binary):
- '''
- Mock whereis_exe, so that it looks like
- X11 xwd binary is present on the system.
- '''
- return binary == 'xwd'
-
- @staticmethod
- def call(args, stdout):
- '''
- Mocked subprocess.call to check console parameters.
- '''
- assert len(args) == 3, args
- assert args[0] == 'xwd', args
- assert args[1:] == ['-silent', '-root'], args
- assert stdout.name == join(
- expanduser('~'), 'Pictures', 'screenshot.xwd'
- ), stdout.name
- with open(stdout.name, 'w') as scr:
- scr.write('')
-
-
-class TestScreenshot(unittest.TestCase):
- '''
- TestCase for plyer.screenshot.
- '''
-
- def setUp(self):
- path = join(expanduser('~'), 'Pictures')
- if not exists(path):
- mkdir(path)
-
- @PlatformTest('macosx')
- def test_screenshot_screencapture(self):
- '''
- Test mocked MacOS screencapture for plyer.screenshot.
- '''
- scr = platform_import(
- platform='macosx',
- module_name='screenshot',
- whereis_exe=MockedScreenCapture.whereis_exe
- )
-
- # such class exists in screenshot module
- self.assertIn('OSXScreenshot', dir(scr))
-
- # the required instance is created
- scr = scr.instance()
- self.assertIn('OSXScreenshot', str(scr))
-
- # move capture from context manager to run without mock
- with patch(target='subprocess.call', new=MockedScreenCapture.call):
- self.assertIsNone(scr.capture())
-
- self.assertTrue(exists(scr.file_path))
- remove(scr.file_path)
-
- @PlatformTest('linux')
- def test_screenshot_xwd(self):
- '''
- Test mocked X11 xwd for plyer.screenshot.
- '''
- scr = platform_import(
- platform='linux',
- module_name='screenshot',
- whereis_exe=MockedXWD.whereis_exe
- )
-
- # such class exists in screenshot module
- self.assertIn('LinuxScreenshot', dir(scr))
-
- # the required instance is created
- scr = scr.instance()
- self.assertIn('LinuxScreenshot', str(scr))
-
- # move capture from context manager to run without mock
- with patch(target='subprocess.call', new=MockedXWD.call):
- self.assertIsNone(scr.capture())
-
- self.assertTrue(exists(scr.file_path))
- remove(scr.file_path)
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_storagepath.py b/sbapp/plyer/tests/test_storagepath.py
deleted file mode 100644
index 35b8139e..00000000
--- a/sbapp/plyer/tests/test_storagepath.py
+++ /dev/null
@@ -1,72 +0,0 @@
-'''
-TestStoragePath
-===============
-
-Tested platforms:
-
-* macOS
-'''
-
-import unittest
-
-from plyer.tests.common import platform_import, PlatformTest
-
-
-class TestStoragePath(unittest.TestCase):
- '''
- TestCase for plyer.storagepath.
- '''
-
- @PlatformTest('macosx')
- def test_storagepath_macosx(self):
- '''
- Test macOS for plyer.storagepath.
- '''
- storagepath = platform_import(
- platform='macosx',
- module_name='storagepath'
- )
-
- self.assertIn('OSXStoragePath', dir(storagepath))
- storagepath = storagepath.instance()
- self.assertIn('OSXStoragePath', str(storagepath))
-
- path_format = 'file:///Users/'
-
- self.assertIn(path_format, storagepath.get_home_dir())
- self.assertIn('/', storagepath.get_root_dir())
- self.assertIn(path_format, storagepath.get_documents_dir())
- self.assertIn(path_format, storagepath.get_downloads_dir())
- self.assertIn(path_format, storagepath.get_videos_dir())
- self.assertIn(path_format, storagepath.get_music_dir())
- self.assertIn(path_format, storagepath.get_pictures_dir())
- self.assertIn(path_format, storagepath.get_application_dir())
-
- @PlatformTest('win')
- def test_storagepath_windows(self):
- '''
- Test win for plyer.storagepath.
- '''
- storagepath = platform_import(
- platform='win',
- module_name='storagepath'
- )
-
- self.assertIn('WinStoragePath', dir(storagepath))
- storagepath = storagepath.instance()
- self.assertIn('WinStoragePath', str(storagepath))
-
- path_format = ':\\'
-
- self.assertIn(path_format, storagepath.get_home_dir())
- self.assertIn(path_format, storagepath.get_root_dir())
- self.assertIn(path_format, storagepath.get_documents_dir())
- self.assertIn(path_format, storagepath.get_downloads_dir())
- self.assertIn(path_format, storagepath.get_videos_dir())
- self.assertIn(path_format, storagepath.get_music_dir())
- self.assertIn(path_format, storagepath.get_pictures_dir())
- self.assertIn(path_format, storagepath.get_application_dir())
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_uniqueid.py b/sbapp/plyer/tests/test_uniqueid.py
deleted file mode 100644
index 629c840a..00000000
--- a/sbapp/plyer/tests/test_uniqueid.py
+++ /dev/null
@@ -1,78 +0,0 @@
-'''
-TestUniqueID
-============
-
-Tested platforms:
-
-* Windows
-'''
-
-import unittest
-from mock import patch, Mock
-from plyer.tests.common import PlatformTest, platform_import
-
-
-class TestUniqueID(unittest.TestCase):
- '''
- TestCase for plyer.uniqueid.
- '''
-
- def test_uniqueid(self):
- '''
- General all platform test for plyer.uniqueid.
- '''
- from plyer import uniqueid
- self.assertTrue(len(uniqueid.id) > 0)
-
- @PlatformTest('win')
- def test_uniqueid_win(self):
- '''
- Test Windows API for plyer.uniqueid.
- '''
- try:
- from winreg import (
- HKEY_LOCAL_MACHINE as HKLM,
- KEY_READ as READ, KEY_WOW64_64KEY as VIEW
- )
- except ImportError:
- from _winreg import (
- HKEY_LOCAL_MACHINE as HKLM,
- KEY_READ as READ, KEY_WOW64_64KEY as VIEW
- )
-
- # mock the 'regedit' alias for winreg,
- # see if the import passes and get the instance
- regedit_mod = 'plyer.platforms.win.uniqueid.regedit'
- with patch(target=regedit_mod):
- uniqueid_ = platform_import(
- platform='win',
- module_name='uniqueid'
- )
- uniqueid = uniqueid_.instance()
- self.assertIsInstance(uniqueid_.regedit, Mock)
-
- # out of mocking block, regedit should be a winreg module
- self.assertIsInstance(uniqueid_.regedit, type(unittest))
-
- # OpenKey is supposed to return a handle to registry key
- regedit_opkey = 'plyer.platforms.win.uniqueid.regedit.OpenKey'
- with patch(target=regedit_opkey, return_value='unicorn') as opkey:
-
- # QueryValueEx is supposed to return 2 packed values
- # (key, type_id)
- queryval = 'plyer.platforms.win.uniqueid.regedit.QueryValueEx'
- retval = ('unique', None)
- with patch(target=queryval, return_value=retval) as query:
- uid = uniqueid.id
- opkey.assert_called_once_with(
- # key, subkey
- HKLM, r'SOFTWARE\\Microsoft\\Cryptography',
- # reserved integer (has to be 0 - zero), access mask
- 0, READ | VIEW
- )
- query.assert_called_once_with('unicorn', 'MachineGuid')
- self.assertEqual(uid, retval[0])
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/tests/test_utils.py b/sbapp/plyer/tests/test_utils.py
deleted file mode 100644
index 3532b76b..00000000
--- a/sbapp/plyer/tests/test_utils.py
+++ /dev/null
@@ -1,417 +0,0 @@
-'''
-TestUtils
-=========
-
-Tested platforms:
-
-* Android
-* iOS
-* Windows
-* MacOS
-* Linux
-'''
-
-import unittest
-from mock import patch
-
-
-class TestUtils(unittest.TestCase):
- '''
- TestCase for plyer.utils.
- '''
-
- def cutter(self, part, string):
- '''
- Cut off a part of a string if it contains a substring,
- otherwise raise an error.
- '''
- self.assertIn(part, string)
- return string[len(part):]
-
- def test_deprecated_function(self):
- '''
- Test printed out warning with @deprecated decorator
- on a function without any arguments.
- '''
-
- from plyer.utils import deprecated
-
- @deprecated
- def function():
- '''
- Dummy deprecated function.
- '''
- return 1
-
- with patch(target='warnings.warn') as stderr:
- self.assertEqual(function(), 1)
-
- args, _ = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('deprecated function function', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('by test_deprecated_function().\n', args)
-
- args, _ = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated function.
- ''',
- ))
-
- def test_deprecated_function_arg(self):
- '''
- Test printed out warning with @deprecated decorator
- on a function with arguments.
- '''
-
- from plyer.utils import deprecated
-
- @deprecated
- def function_with_arg(arg):
- '''
- Dummy deprecated function with arg.
- '''
- return arg
-
- with patch(target='warnings.warn') as stderr:
- self.assertEqual(function_with_arg(1), 1)
-
- args, _ = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('deprecated function function_with_arg', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('by test_deprecated_function_arg().\n', args)
-
- args, _ = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated function with arg.
- ''',
- ))
-
- def test_deprecated_function_kwarg(self):
- '''
- Test printed out warning with @deprecated decorator
- on a function with keyword arguments.
- '''
-
- from plyer.utils import deprecated
-
- @deprecated
- def function_with_kwarg(kwarg):
- '''
- Dummy deprecated function with kwarg.
- '''
- return kwarg
-
- with patch(target='warnings.warn') as stderr:
- self.assertEqual(function_with_kwarg(kwarg=1), 1)
-
- args, _ = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('deprecated function function_with_kwarg', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('by test_deprecated_function_kwarg().\n', args)
-
- args, _ = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated function with kwarg.
- ''',
- ))
-
- def test_deprecated_class_method(self):
- '''
- Test printed out warning with @deprecated decorator
- on a instance bound method.
- '''
-
- from plyer.utils import deprecated
-
- class Class:
- '''
- Dummy class with deprecated method method.
- '''
- def __init__(self, *args, **kwargs):
- self.args = args
- self.kwargs = kwargs
-
- @deprecated
- def method(self):
- '''
- Dummy deprecated method.
- '''
- return (self.args, self.kwargs)
-
- with patch(target='warnings.warn') as stderr:
- args = (1, 2, 3)
- kwargs = dict(x=1, y=2)
-
- cls = Class(*args, **kwargs)
- self.assertEqual(cls.method(), (args, kwargs))
-
- args, kwargs = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('deprecated function method', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('by test_deprecated_class_method().\n', args)
-
- args, kwargs = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated method.
- ''',
- ))
-
- def test_deprecated_class_static_none(self):
- '''
- Test printed out warning with @deprecated decorator
- on a static method without arguments.
- '''
-
- from plyer.utils import deprecated
-
- class Class:
- '''
- Dummy class with deprecated static method.
- '''
- args = None
- kwargs = None
-
- def __init__(self, *args, **kwargs):
- Class.args = args
- Class.kwargs = kwargs
-
- @staticmethod
- @deprecated
- def static():
- '''
- Dummy deprecated static method.
- '''
- return (Class.args, Class.kwargs)
-
- with patch(target='warnings.warn') as stderr:
- self.assertEqual(Class.static(), (None, None))
-
- args, _ = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('deprecated function static', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter(
- 'by test_deprecated_class_static_none().\n', args
- )
-
- args, _ = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated static method.
- ''',
- ))
-
- def test_deprecated_class_static_argskwargs(self):
- '''
- Test printed out warning with @deprecated decorator
- on a static method with arguments and keyword argument.
- '''
-
- from plyer.utils import deprecated
-
- class Class:
- '''
- Dummy class with deprecated static method.
- '''
- args = None
- kwargs = None
-
- def __init__(self, *args, **kwargs):
- Class.args = args
- Class.kwargs = kwargs
-
- @staticmethod
- @deprecated
- def static():
- '''
- Dummy deprecated static method.
- '''
- return (Class.args, Class.kwargs)
-
- with patch(target='warnings.warn') as stderr:
- args = (1, 2, 3)
- kwargs = dict(x=1, y=2)
-
- cls = Class(*args, **kwargs)
- self.assertEqual(cls.static(), (args, kwargs))
-
- args, kwargs = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('deprecated function static', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter(
- 'by test_deprecated_class_static_argskwargs().\n', args
- )
-
- args, kwargs = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated static method.
- ''',
- ))
-
- def test_deprecated_class_clsmethod(self):
- '''
- Test printed out warning with @deprecated decorator
- on a class bound method.
- '''
-
- from plyer.utils import deprecated
-
- class Class:
- '''
- Dummy class with deprecated class method.
- '''
- args = None
- kwargs = None
-
- @classmethod
- @deprecated
- def clsmethod(cls):
- '''
- Dummy deprecated class method.
- '''
- return (cls.args, cls.kwargs)
-
- with patch(target='warnings.warn') as stderr:
- self.assertEqual(Class.clsmethod(), (None, None))
-
- args, _ = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('deprecated function clsmethod', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('by test_deprecated_class_clsmethod().\n', args)
-
- args, _ = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated class method.
- ''',
- ))
-
- def test_deprecated_class(self):
- '''
- Test printed out warning with @deprecated decorator on a class.
- '''
-
- from plyer.utils import deprecated
-
- @deprecated
- class Class:
- '''
- Dummy deprecated class.
- '''
- def __init__(self, *args, **kwargs):
- self.args = args
- self.kwargs = kwargs
-
- with patch(target='warnings.warn') as stderr:
- args = (1, 2, 3)
- kwargs = dict(x=1, y=2)
-
- cls = Class(*args, **kwargs)
- self.assertIsInstance(cls, Class)
- self.assertEqual(cls.args, args)
- self.assertEqual(cls.kwargs, kwargs)
-
- args, _ = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('Creating an instance', args)
- args = self.cutter('deprecated class Class in', args)
- args = self.cutter(__name__, args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('by test_deprecated_class().\n', args)
-
- args, kwargs = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated class.
- ''',
- ))
-
- def test_deprecated_class_inherited(self):
- '''
- Test printed out warning with @deprecated decorator on a class
- which inherits from a deprecated class.
- '''
-
- from plyer.utils import deprecated
-
- @deprecated
- class Class:
- '''
- Dummy deprecated class.
- '''
- def __init__(self, *args, **kwargs):
- self.args = args
- self.kwargs = kwargs
-
- class Inherited(Class):
- '''
- Dummy class inheriting from a dummy deprecated class.
- '''
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.args = args
- self.kwargs = kwargs
-
- with patch(target='warnings.warn') as stderr:
- args = (1, 2, 3)
- kwargs = dict(x=1, y=2)
-
- cls = Inherited(*args, **kwargs)
- self.assertIsInstance(cls, Inherited)
- self.assertEqual(cls.args, args)
- self.assertEqual(cls.kwargs, kwargs)
-
- args, _ = stderr.call_args_list[0]
- args = args[0]
- args = self.cutter('[WARNING] ', args)
- args = self.cutter('Creating an instance', args)
- args = self.cutter('deprecated class Class in', args)
- args = self.cutter(__name__, args)
- args = self.cutter('Called from', args)
- args = self.cutter('test_utils.py', args)
- args = self.cutter('by test_deprecated_class_inherited().\n', args)
-
- args, kwargs = stderr.call_args_list[1]
- self.assertEqual(args, (
- '''
- Dummy deprecated class.
- ''',
- ))
-
-
-if __name__ == '__main__':
- unittest.main()

diff --git a/sbapp/plyer/utils.py b/sbapp/plyer/utils.py
index 101763e9..573075de 100644
--- a/sbapp/plyer/utils.py
+++ b/sbapp/plyer/utils.py
@@ -9,6 +9,7 @@ from os import environ
from os import path
from sys import platform as _sys_platform
import sys
+import RNS
class Platform:
@@ -91,8 +92,10 @@ class Proxy:
# do the import
try:
name = object.__getattribute__(self, '_name')
- module = 'plyer.platforms.{}.{}'.format(
- platform, name)
+ if RNS.vendor.platformutils.is_android():
+ module = 'plyer.platforms.{}.{}'.format(platform, name)
+ else:
+ module = 'sbapp.plyer.platforms.{}.{}'.format(platform, name)
mod = __import__(module, fromlist='.')
obj = mod.instance()
except Exception:


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────